<commit_before>var assert = require('assert');
var tempoiq = require('../lib/tempoiq');
var creds = {};

if (process.env.SNIPPET) {
  creds = require('./snippet-credentials.json');
}

var getClient = function() {
    var client = tempoiq.Client(creds.key, creds.secret, creds.hostname);
    return client;
}

var initialize = function(done_cb) {
  this.timeout(30000);
  console.log("Starting data initialization");
  var exec = require('child_process').exec;
  exec('python ./test/example-data/initialize.py -n ' + creds.hostname +
               ' -k ' + creds.key + ' -s ' + creds.secret,
      function(err, stdout, stderr) {
        if (err) {
          console.log("error! stdout:" + stdout.toString());
          console.log("stderr: " + stderr.toString());
        }
        done_cb(err);
      }
  );
}

describe("Example code snippet tests", function() {
  if (!process.env.SNIPPET) {
    // skip em
    return;
  }

  before(initialize);

  it("create-device", function(done) {
    var client = getClient();

    // snippet-begin create-device
    client.createDevice(new tempoiq.Device("thermostat.0",
      {
        name: "Test Thermostat",
        attributes: {
          model: "v1"
        },
        sensors: [
          new tempoiq.Sensor("temperature"),
          new tempoiq.Sensor("humidity")
        ]
      }),
      function(err, device) {
        if (err) throw err;
        // Successfully created device
        done();     // snippet-ignore
      }
    );
    // snippet-end
  });

  it('read-data-one-device', function(done) {
    var client = getClient();
    // snippet-begin read-data-one-device
    var selection = {
      devices: {
        key: "thermostat.0"
      }
    };
    var start = "2015-01-01T00:00:00Z";
    var end = "2015-01-01T01:00:00Z";

    client.read(selection, start, end, null, function(err, data) {
      data.forEach(function(row) {
        var timestamp = row.ts;
        var values = [];        // List of sensor values at a timestamp
        for (var device in row.values) {
            for (var sensor in row.values[device]) {
                values.push(row.values[device][sensor]);
            }
        }
        assert(values.length > 0);  // snippet-ignore
      });
      assert(data.length > 0);    // snippet-ignore
      done(err);                  // snippet-ignore
    });
    // snippet-end
  });

  it("single-point", function(done) {
    var client = getClient();

    // snippet-begin single-point
    var ts = new Date("2015-01-10T00:00:00Z");
    var selection = {
      devices: { key: "thermostat.1" },
      sensors: { key: "temperature" }
    };

    client.single(selection, "before", ts, null, function(err, data) {
      if (err) {
        // handle error
      } else {
        if (data.length == 0) {
          // No point found
          err = "No point found!"       // snippet-ignore  Be sure to test the code path with a found point
        } else {
          var row = data[0];
          var point_timestamp = row.ts;
          var point_value = row.value('thermostat.1', 'temperature');
          assert.equal(point_timestamp.getFullYear(), 2015);     // snippet-ignore
          // do something
        }
      }
      done(err);                           // snippet-ignore
    });
    // snippet-end
  });
});

<commit_msg>Add write-data snippet<commit_after>var assert = require('assert');
var tempoiq = require('../lib/tempoiq');
var creds = {};

if (process.env.SNIPPET) {
  creds = require('./snippet-credentials.json');
}

var getClient = function() {
    var client = tempoiq.Client(creds.key, creds.secret, creds.hostname);
    return client;
}

var initialize = function(done_cb) {
  this.timeout(30000);
  console.log("Starting data initialization");
  var exec = require('child_process').exec;
  exec('python ./test/example-data/initialize.py -n ' + creds.hostname +
               ' -k ' + creds.key + ' -s ' + creds.secret,
      function(err, stdout, stderr) {
        if (err) {
          console.log("error! stdout:" + stdout.toString());
          console.log("stderr: " + stderr.toString());
        }
        done_cb(err);
      }
  );
}

describe("Example code snippet tests", function() {
  if (!process.env.SNIPPET) {
    // skip em
    return;
  }

  before(initialize);

  it("create-device", function(done) {
    var client = getClient();

    // snippet-begin create-device
    client.createDevice(new tempoiq.Device("thermostat.0",
      {
        name: "Test Thermostat",
        attributes: {
          model: "v1"
        },
        sensors: [
          new tempoiq.Sensor("temperature"),
          new tempoiq.Sensor("humidity")
        ]
      }),
      function(err, device) {
        if (err) throw err;
        // Successfully created device
        done();     // snippet-ignore
      }
    );
    // snippet-end
  });

  it("write-data", function(done) {
    var client = getClient();

    // snippet-begin write-data
    var device = "thermostat.0";
    var t1 = new Date("2015-01-01T00:00:00Z");
    var t2 = new Date("2015-01-01T00:00:05Z");

    var data = new tempoiq.BulkWrite();

    data.push(device, "temperature",
              new tempoiq.DataPoint(t1, 68));
    data.push(device, "temperature",
              new tempoiq.DataPoint(t2, 67.5));
    data.push(device, "humidity",
              new tempoiq.DataPoint(t1, 71.5));
    data.push(device, "humidity",
              new tempoiq.DataPoint(t2, 70));

    client.writeBulk(data, function(err) {
        if (err) throw err;
        done();     // snippet-ignore
    });
    // snippet-end
  });

  it('read-data-one-device', function(done) {
    var client = getClient();
    // snippet-begin read-data-one-device
    var selection = {
      devices: {
        key: "thermostat.0"
      }
    };
    var start = new Date("2015-01-01T00:00:00Z");
    var end = new Date("2015-01-01T01:00:00Z");

    client.read(selection, start, end, null, function(err, data) {
      if (err) throw err;
      data.forEach(function(row) {
        var timestamp = row.ts;
        var values = [];        // List of sensor values at a timestamp
        for (var device in row.values) {
            for (var sensor in row.values[device]) {
                values.push(row.values[device][sensor]);
            }
        }
        assert(values.length > 0);  // snippet-ignore
      });
      assert(data.length > 0);    // snippet-ignore
      done(err);                  // snippet-ignore
    });
    // snippet-end
  });

  it("single-point", function(done) {
    var client = getClient();

    // snippet-begin single-point
    var ts = new Date("2015-01-10T00:00:00Z");
    var selection = {
      devices: { key: "thermostat.1" },
      sensors: { key: "temperature" }
    };

    client.single(selection, "before", ts, null, function(err, data) {
      if (err) {
        // handle error
      } else {
        if (data.length == 0) {
          // No point found
          err = "No point found!"       // snippet-ignore  Be sure to test the code path with a found point
        } else {
          var row = data[0];
          var point_timestamp = row.ts;
          var point_value = row.value('thermostat.1', 'temperature');
          assert.equal(point_timestamp.getFullYear(), 2015);     // snippet-ignore
          // do something
        }
      }
      done(err);                           // snippet-ignore
    });
    // snippet-end
  });
});

<|endoftext|>


<commit_before>/* Implement AI here */

var state = new State();
console.log("AI's target", state.comMission);

// This is the 'class' of State
function State() {
    this.comActions = [0, 0, 1, 2, 3, 4, 4]; // actions of computer player remained
    this.humActions = [0, 0, 1, 2, 3, 4, 4]; // actions of human player remained
    this.comMission = [1, 3, 4]; // the AI's mission
    this.humMission = [0, 1, 2];
    this.tikiOrder = [0, 1, 2, 3, 4, 5, 6, 7, 8];
    this.init = function() {
        this.comActions = [0, 0, 1, 2, 3, 4, 4];
        this.humActions = [0, 0, 1, 2, 3, 4, 4];
        // TODO: move createMission in main.js to here
    }
}

function copyState( stateToBeCopied,targetState)
{
    stateToBeCopied.comActions = targetState.comActions.slice(0);
    stateToBeCopied.humActions = targetState.humActions.slice(0);
    stateToBeCopied.tikiOrder = targetState.tikiOrder.slice(0);
    stateToBeCopied.comMission = targetState.comMission.slice(0);
    stateToBeCopied.humMission = targetState.humMission.slice(0);
}

/*
 * Update state object.
 *
 * action: index of new action (card used)
 * tikiIdx: tiki index on which the action applied
 * player: player who conducts the action (0: AI, 1: human player)
 * tikiId: is the identity of the tiki , it represent the tiki
 */
function updateState(action, tikiIdx, player) {
    //console.log('Hey, I receive action', action, 'on Tiki #' + tikiIdx);
    var tikiId = state.tikiOrder[tikiIdx]

    if (0 === player) {
        var idx = state.comActions.indexOf(action);
        state.comActions.splice(idx, 1);
        console.log('This is AI do the action', action, tikiIdx)
        if (action == 0) {
            updateStateWithMoveup(1,tikiId , state);
            moveUp(tikiIdx, 1);
        }
        else if (action == 1) {
            updateStateWithMoveup(2,tikiId, state);
            moveUp(tikiIdx, 2);
        }
        else if (action == 2) {
            updateStateWithMoveup(3,tikiId, state);
            moveUp(tikiIdx, 3);
        }
        else if (action == 3) {
            updateStateWithPush(tikiId, state);
            moveToBottom(tikiIdx);
        }
        else if (action == 4) {
            updateStateWithKill(state);
            killLast();
        }
    }
    else if (1 === player) {

        var idx = state.humActions.indexOf(action);
        state.humActions.splice(idx, 1);
        console.log('This is human do the action')

        if (action == 0)
            updateStateWithMoveup(1,tikiId , state);
        else if (action == 1)
            updateStateWithMoveup(2,tikiId, state);
        else if (action == 2)
            updateStateWithMoveup(3,tikiId, state);
        else if (action == 3)
            updateStateWithPush(tikiId, state);
        else if (action == 4)
            updateStateWithKill(state);
    }

    else {
        console.log('ERROR: invalid "player" passed to updateState()')
    }

    console.log('Current Order:', state.tikiOrder)

    checkGameEnd();
}

//used for AI get nextState in ComAI()
function tryNextState(action , tikiId , currentState)
{

    if (action == 0)
        updateStateWithMoveup(1,tikiId , currentState);
    else if (action == 1)
        updateStateWithMoveup(2,tikiId, currentState);
    else if (action == 2)
        updateStateWithMoveup(3,tikiId, currentState);
    else if (action == 3)
        updateStateWithPush(tikiId, currentState);
    else if (action == 4)
        updateStateWithKill(currentState);

    return currentState
}

function updateStateWithMoveup(moveup , tikiId , targetState)
{

    var tmp = tikiId
    var moveTime = 0
    var tikiIndex = getTikiIndex(tikiId , targetState)

    while(moveTime<moveup){
        var index = tikiIndex - moveTime
        targetState.tikiOrder[index] = targetState.tikiOrder[index-1]
        moveTime +=1
    }
    targetState.tikiOrder[tikiIndex - moveup] = tmp;
}

function updateStateWithPush(tikiId , targetState)
{

    var tikiIndex = getTikiIndex(tikiId , targetState);
    var index = tikiIndex;

    while(index<targetState.tikiOrder.length-1){
        targetState.tikiOrder[index] = targetState.tikiOrder[index+1];
        index +=1;
    }

    targetState.tikiOrder[targetState.tikiOrder.length-1] = tikiId;

}

function updateStateWithKill(targetState)
{

    targetState.tikiOrder.pop();

}

function evaluationFunction(currentState)
{
    var score = computeAISocre(currentState);
    var distance = 0;
    var idx;

    for( var i = 0 ; i < currentState.comMission.length ; i++){

        idx = getTikiIndex(currentState.comMission[i] , currentState);
      /*if(idx <= i)
        {

            if (i == 0) score+=9
            else if (i == 1) score+=5
            else if (i == 2) score +=2
        }*/
        if(idx > i) {
            distance += Math.abs(idx-i);
        }
    }

    return score*10 - distance;
}

//compute the states of every remained action and to every tiki on the table
//use evaluation function to get the score , return the best action
function comAIMove()
{
    //console.log('Computer AI turn')

    var allNextState = []
    var allNextAction = []
    var allNextMovingTiki = []
    for(var i = 0 ; i < state.comActions.length ; i++)
        for(var j = 0 ; j < state.tikiOrder.length ; j++)
        {
            var nextState = new State()
            copyState(nextState,state)
            //console.log("try state " , state , "with op and tikiOrder " , state.comActions[i] , state.tikiOrder[j])
            var illegal = checkLegalAction(state.comActions[i] , state.tikiOrder[j] , nextState)

            if (illegal == 1)
                continue

            nextState = tryNextState(state.comActions[i] , state.tikiOrder[j] , nextState)
            allNextState.push(nextState)
            allNextAction.push(i)
            allNextMovingTiki.push(j)

            //console.log("The nextState oeder is " , nextState.tikiOrder)

        }

    var maxIndex = 0 ;
    var maxValue = -100000 ;

    for(var i = 0 ; i < allNextState.length ; i++)
    {
        var tmpValue = evaluationFunction(allNextState[i])
        if(tmpValue > maxValue)
            {maxValue = tmpValue
             maxIndex = i
            }
    }

    console.log("AI say do operation " , state.comActions[allNextAction[maxIndex]] , " on tiki " , state.tikiOrder[allNextMovingTiki[maxIndex]])
    var operation = state.comActions[allNextAction[maxIndex]];
    updateState(operation, allNextMovingTiki[maxIndex], 0);

    var AIscore = computeAISocre(state)
    console.log("AI now scores ", AIscore)

}

//check if the action is legal to the tiki
//ex: action push up 3 is not allowed for tiki of order 0
//p.s. kill is always allowed
function checkLegalAction( action,tikiId , targetState)
{

    index = getTikiIndex(tikiId , targetState)

    var illegal = 0
    if(action == 0)
    {
        if(index == 0)
            illegal = 1
    }
    else if(action ==1)
    {
        if(index <=1)
            illegal = 1
    }
    else if(action ==2)
    {
        if(index <= 2)
            illegal = 1
    }

    else if(action == 3)
    {
        if (index >= targetState.tikiOrder.length-1)
            illegal = 1
    }

    //console.log("In checkLegalAction , The tikiIdx , index , action , illegal " , tikiIdx , index , action , illegal)

    return illegal

}

//give a tiki id , it return the tiki 's order in array tikiOrder
function getTikiIndex(tikiId , targetState)
{
    var idx = 0;
    for(var i = 0 ; i < targetState.tikiOrder.length ; i++)
        if (targetState.tikiOrder[i] == tikiId)
            {idx = i ;
            return idx;}

     //console.log('The tiki you try to find is not in tikiOrder , which may be killed...')
     //console.log('Tiki Order is ' , targetState.tikiOrder)
     //console.log('The tikiOrder ', tikiIdx , ' , which is not in tikiOrder!')
}

function computeAISocre(currentState)
{
    var score = 0
    for( var i = 0 ; i < currentState.comMission.length ; i++){

      idx = getTikiIndex(currentState.comMission[i] , currentState);
      if(idx <= i)
        {

            if (i == 0) score+=9
            else if (i == 1) score+=5
            else if (i == 2) score +=2
        }
    }

   return score
}

function computeHumSocre(currentState)
{
    var score = 0;
    var reward = [9, 5, 2];
    for (var i = 0; i < currentState.humMission.length; i++) {
        idx = currentState.tikiOrder.indexOf(currentState.humMission[i]);
        if (idx <= i && idx !== -1) {
            score += reward[i];
        }
    }
   return score;
}

function checkGameEnd() {
    if ((0 === state.humActions.length && 0 === state.comActions.length) || state.tikiOrder.length <= 3) {
        computeResult();
    }
}

function computeResult() {
    var comScore = computeAISocre(state), humScore = computeHumSocre(state);
    var winner = (comScore > humScore)? 'Computer' : 'You';
    alert('Your score: ' + humScore +
          '\nComputer\'s score: ' + comScore +
          '\n' + winner + ' Wins');
}
<commit_msg>Add Agent.js<commit_after>/* Implement AI here */

var state = new State();

// This is the 'class' of State
function State() {
    this.comActions = [0, 0, 1, 2, 3, 4, 4]; // actions of computer player remained
    this.humActions = [0, 0, 1, 2, 3, 4, 4]; // actions of human player remained
    this.comMission = [1, 3, 4]; // the AI's mission
    this.humMission = [0, 1, 2];
    this.tikiOrder = [0, 1, 2, 3, 4, 5, 6, 7, 8];
    this.init = function() {
        this.comActions = [0, 0, 1, 2, 3, 4, 4];
        this.humActions = [0, 0, 1, 2, 3, 4, 4];
        // TODO: move createMission in main.js to here
    }
}

function copyState( stateToBeCopied,targetState)
{
    stateToBeCopied.comActions = targetState.comActions.slice(0);
    stateToBeCopied.humActions = targetState.humActions.slice(0);
    stateToBeCopied.tikiOrder = targetState.tikiOrder.slice(0);
    stateToBeCopied.comMission = targetState.comMission.slice(0);
    stateToBeCopied.humMission = targetState.humMission.slice(0);
}

/*
 * Update state object.
 *
 * action: index of new action (card used)
 * tikiIdx: tiki index on which the action applied
 * player: player who conducts the action (0: AI, 1: human player)
 * tikiId: is the identity of the tiki , it represent the tiki
 */
function updateState(action, tikiIdx, player) {
    //console.log('Hey, I receive action', action, 'on Tiki #' + tikiIdx);
    var tikiId = state.tikiOrder[tikiIdx]

    if (0 === player) {
        var idx = state.comActions.indexOf(action);
        state.comActions.splice(idx, 1);
        console.log('This is AI do the action', action, tikiIdx)
        if (action == 0) {
            updateStateWithMoveup(1,tikiId , state);
            moveUp(tikiIdx, 1);
        }
        else if (action == 1) {
            updateStateWithMoveup(2,tikiId, state);
            moveUp(tikiIdx, 2);
        }
        else if (action == 2) {
            updateStateWithMoveup(3,tikiId, state);
            moveUp(tikiIdx, 3);
        }
        else if (action == 3) {
            updateStateWithPush(tikiId, state);
            moveToBottom(tikiIdx);
        }
        else if (action == 4) {
            updateStateWithKill(state);
            killLast();
        }
    }
    else if (1 === player) {

        var idx = state.humActions.indexOf(action);
        state.humActions.splice(idx, 1);
        console.log('This is human do the action')

        if (action == 0)
            updateStateWithMoveup(1,tikiId , state);
        else if (action == 1)
            updateStateWithMoveup(2,tikiId, state);
        else if (action == 2)
            updateStateWithMoveup(3,tikiId, state);
        else if (action == 3)
            updateStateWithPush(tikiId, state);
        else if (action == 4)
            updateStateWithKill(state);
    }

    else {
        console.log('ERROR: invalid "player" passed to updateState()')
    }

    console.log('Current Order:', state.tikiOrder)

    checkGameEnd();
}

//used for AI get nextState in ComAI()
function tryNextState(action , tikiId , currentState)
{

    if (action == 0)
        updateStateWithMoveup(1,tikiId , currentState);
    else if (action == 1)
        updateStateWithMoveup(2,tikiId, currentState);
    else if (action == 2)
        updateStateWithMoveup(3,tikiId, currentState);
    else if (action == 3)
        updateStateWithPush(tikiId, currentState);
    else if (action == 4)
        updateStateWithKill(currentState);

    return currentState
}

function updateStateWithMoveup(moveup , tikiId , targetState)
{

    var tmp = tikiId
    var moveTime = 0
    var tikiIndex = getTikiIndex(tikiId , targetState)

    while(moveTime<moveup){
        var index = tikiIndex - moveTime
        targetState.tikiOrder[index] = targetState.tikiOrder[index-1]
        moveTime +=1
    }
    targetState.tikiOrder[tikiIndex - moveup] = tmp;
}

function updateStateWithPush(tikiId , targetState)
{

    var tikiIndex = getTikiIndex(tikiId , targetState);
    var index = tikiIndex;

    while(index<targetState.tikiOrder.length-1){
        targetState.tikiOrder[index] = targetState.tikiOrder[index+1];
        index +=1;
    }

    targetState.tikiOrder[targetState.tikiOrder.length-1] = tikiId;

}

function updateStateWithKill(targetState)
{

    targetState.tikiOrder.pop();

}

function evaluationFunction(currentState)
{
    var score = computeAISocre(currentState);
    var distance = 0;
    var idx;

    for( var i = 0 ; i < currentState.comMission.length ; i++){

        idx = getTikiIndex(currentState.comMission[i] , currentState);
      /*if(idx <= i)
        {

            if (i == 0) score+=9
            else if (i == 1) score+=5
            else if (i == 2) score +=2
        }*/
        if(idx > i) {
            distance += Math.abs(idx-i);
        }
    }

    return score*10 - distance;
}

//compute the states of every remained action and to every tiki on the table
//use evaluation function to get the score , return the best action
function comAIMove()
{
    //console.log('Computer AI turn')

    var allNextState = []
    var allNextAction = []
    var allNextMovingTiki = []
    // get legal actions
    for(var i = 0 ; i < state.comActions.length ; i++)
        for(var j = 0 ; j < state.tikiOrder.length ; j++)
        {
            var nextState = new State()
            copyState(nextState,state)
            //console.log("try state " , state , "with op and tikiOrder " , state.comActions[i] , state.tikiOrder[j])
            var illegal = checkLegalAction(state.comActions[i] , state.tikiOrder[j] , nextState)

            if (illegal == 1)
                continue

            nextState = tryNextState(state.comActions[i] , state.tikiOrder[j] , nextState)
            allNextState.push(nextState)
            allNextAction.push(i)
            allNextMovingTiki.push(j)

            //console.log("The nextState oeder is " , nextState.tikiOrder)

        }

    var maxIndex = 0 ;
    var maxValue = -100000 ;

    for(var i = 0 ; i < allNextState.length ; i++)
    {
        var tmpValue = evaluationFunction(allNextState[i])
        if(tmpValue > maxValue)
            {maxValue = tmpValue
             maxIndex = i
            }
    }

    console.log("AI say do operation " , state.comActions[allNextAction[maxIndex]] , " on tiki " , state.tikiOrder[allNextMovingTiki[maxIndex]])
    var operation = state.comActions[allNextAction[maxIndex]];
    updateState(operation, allNextMovingTiki[maxIndex], 0);

    var AIscore = computeAISocre(state)
    console.log("AI now scores ", AIscore)

}

//check if the action is legal to the tiki
//ex: action push up 3 is not allowed for tiki of order 0
//p.s. kill is always allowed
function checkLegalAction( action,tikiId , targetState)
{

    index = getTikiIndex(tikiId , targetState)

    var illegal = 0
    if(action == 0)
    {
        if(index == 0)
            illegal = 1
    }
    else if(action ==1)
    {
        if(index <=1)
            illegal = 1
    }
    else if(action ==2)
    {
        if(index <= 2)
            illegal = 1
    }

    else if(action == 3)
    {
        if (index >= targetState.tikiOrder.length-1)
            illegal = 1
    }

    //console.log("In checkLegalAction , The tikiIdx , index , action , illegal " , tikiIdx , index , action , illegal)

    return illegal

}

//give a tiki id , it return the tiki 's order in array tikiOrder
function getTikiIndex(tikiId , targetState)
{
    var idx = 0;
    for(var i = 0 ; i < targetState.tikiOrder.length ; i++)
        if (targetState.tikiOrder[i] == tikiId)
            {idx = i ;
            return idx;}

     //console.log('The tiki you try to find is not in tikiOrder , which may be killed...')
     //console.log('Tiki Order is ' , targetState.tikiOrder)
     //console.log('The tikiOrder ', tikiIdx , ' , which is not in tikiOrder!')
}

function computeAISocre(currentState)
{
    var score = 0
    for( var i = 0 ; i < currentState.comMission.length ; i++){

      idx = getTikiIndex(currentState.comMission[i] , currentState);
      if(idx <= i)
        {

            if (i == 0) score+=9
            else if (i == 1) score+=5
            else if (i == 2) score +=2
        }
    }

   return score
}

function computeHumSocre(currentState)
{
    var score = 0;
    var reward = [9, 5, 2];
    for (var i = 0; i < currentState.humMission.length; i++) {
        idx = currentState.tikiOrder.indexOf(currentState.humMission[i]);
        if (idx <= i && idx !== -1) {
            score += reward[i];
        }
    }
   return score;
}

function checkGameEnd() {
    if ((0 === state.humActions.length && 0 === state.comActions.length) || state.tikiOrder.length <= 3) {
        computeResult();
    }
}

function computeResult() {
    var comScore = computeAISocre(state), humScore = computeHumSocre(state);
    var winner = (comScore > humScore)? 'Computer' : 'You';
    alert('Your score: ' + humScore +
          '\nComputer\'s score: ' + comScore +
          '\n' + winner + ' Wins');
}
<|endoftext|>


<commit_before>/*
 * Ryan J McGill
 * 2015
 * Web Audio Organ v1.0
 *
 * Licensed under the MIT License
 * http://opensource.org/licenses/mit-license.php
 *
 */
"use strict";

function farfisa(audioContext){
/*
 * Class to generate farfisa keyboard in browser.
 * Pass in a WebAudio audioContext
 */
	this.context = audioContext;
	this.rockers = rockers_structure;
	this.activeRockers = [	"flute4",
							"string",
							"trumpet",
							"oboe",
							"flute8",
							"clarinet",
							"bass"	];

    var getFrequencyOfNote = function (note) {
    /**
     * Get frequency of a given note.
     * @param  {string} note Musical note to convert into hertz.
     * @return {number} Frequency of note in hertz.
     */
        var notes = ['A', 'A#', 'B', 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#'],
            key_number,
            octave;

        if (note.length === 3) {
            octave = note.charAt(2);
        } else {
            octave = note.charAt(1);
        }

        key_number = notes.indexOf(note.slice(0, -1));

        if (key_number < 3) {
            key_number = key_number + 12 + ((octave - 1) * 12) + 1;
        } else {
            key_number = key_number + ((octave - 1) * 12) + 1;
        }

        return 440 * Math.pow(2, (key_number - 49) / 12);
    };

 	var createPlayingNotesStructure = function(){
	/**
     * Create structure to store oscillators of sounding notes
     * @return {object} structure to store sounding oscillators per key
     */
		var playingNotes = {}
		var notes = ['A', 'A#', 'B', 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#'];
			for (var num = 0; num<=5; num++){
				for (var letter = 0; letter < notes.length; letter++){
					var note = notes[letter] + String(num);
					var freq = getFrequencyOfNote(note);
					playingNotes[note] = {
									'frequency':freq,
									'oscillators':[],
									4: note_maps[4][note],
									8: note_maps[8][note],
									16: note_maps[16][note]
								};
				}
			}
		return playingNotes;
	};

	this.playingNotes = createPlayingNotesStructure();

	this.createVoice = function(voice){
	/**
     * Create gain/filter flow for {voice}
     * @param {voice} voice object containing properties for gain/filter flow
     * @return {audioNode} connection point to voice
     */
	    var filter = this.context.createBiquadFilter();
		filter.type = voice.filter.type;
		filter.frequency.value = voice.filter.frequency;
		filter.Q.value = voice.filter.Q;

		var outputGain = this.context.createGain()
		outputGain.gain.value = voice.gain;

		filter.connect(outputGain);
		outputGain.connect(this.context.destination);

	    return filter;
	};

	this.playNote = function(note, rocker){
	/**
     * Create and play all oscillators for {rocker} param on {note}
     * @param {note} key pressed
     * @param {rocker} rocker to play
     */
    	var voices = this.rockers[rocker];

     	for(var voice in voices){
     		var key = {};
     		var audibleNote = this.playingNotes[note][voice];
     		var oscillator = this.context.createOscillator();
     		var gainNode = this.context.createGain();

     		oscillator.frequency.value = this.playingNotes[audibleNote].frequency;
     		oscillator.type = this.rockers[rocker][voice]["waveform"];

     		oscillator.connect(gainNode);
     		gainNode.connect(this.rockers[rocker][voice]["node"]);

     		oscillator.start(0);
     		key.oscillator = oscillator;
     		key.gainNode = gainNode;
     		key.voiceName = rocker + ':' + voice;
     		this.playingNotes[note]["oscillators"].push(key);
     	}
	}

	this.keyDown = function(note){
	/**
     * Interface for farfisa to start playing a note.
     * Plays notes through all rockers included in 'voiceNodes' structure
     */
		//this.playNote(note, "flute4");
		//this.playNote(note, "bass");
		//this.playNote(note, "oboe");
		//this.playNote(note, "trumpet");
		this.playNote(note, "clarinet");
	}

	this.keyUp = function(note){
	/**
     * Interface for farfisa to stop playing all oscillators for note.
     */
    	var keys = this.playingNotes[note].oscillators,
			key, i, now;
		var releaseTime = 0.1;
		for(i=0; keys.length; i++){
			now = this.context.currentTime;
			key = keys.pop();
			key.oscillator.stop(now + releaseTime);

			//create fade out event to avoid key click
			key.gainNode.gain.setValueAtTime(key.gainNode.gain.value, now);
			key.gainNode.gain.exponentialRampToValueAtTime(0.01, now + releaseTime);

			key.oscillator.onended = function(){
			key.oscillator.disconnect();
			key.gainNode.disconnect();
			};
		}
	}
	this.initialize();
};
farfisa.prototype.initialize = function(){
	var rocker, octave, voice;

	for(rocker in this.rockers){
		for(octave in this.rockers[rocker]){
			voice = this.rockers[rocker][octave];
			voice.node = this.createVoice(voice);
		}
	}
};

<commit_msg>adding vibrato<commit_after>/*
 * Ryan J McGill
 * 2015
 * Web Audio Organ v1.0
 *
 * Licensed under the MIT License
 * http://opensource.org/licenses/mit-license.php
 *
 */
"use strict";

function farfisa(audioContext){
/*
 * Class to generate farfisa keyboard in browser.
 * Pass in a WebAudio audioContext
 */
	this.context = audioContext;
	this.rockers = rockers_structure;
	this.activeRockers = [	"flute4",
							"string",
							"trumpet",
							"oboe",
							"flute8",
							"clarinet",
							"bass"	];

    var getFrequencyOfNote = function (note) {
    /**
     * Get frequency of a given note.
     * @param  {string} note Musical note to convert into hertz.
     * @return {number} Frequency of note in hertz.
     */
        var notes = ['A', 'A#', 'B', 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#'],
            key_number,
            octave;

        if (note.length === 3) {
            octave = note.charAt(2);
        } else {
            octave = note.charAt(1);
        }

        key_number = notes.indexOf(note.slice(0, -1));

        if (key_number < 3) {
            key_number = key_number + 12 + ((octave - 1) * 12) + 1;
        } else {
            key_number = key_number + ((octave - 1) * 12) + 1;
        }

        return 440 * Math.pow(2, (key_number - 49) / 12);
    };

 	var createPlayingNotesStructure = function(){
	/**
     * Create structure to store oscillators of sounding notes
     * @return {object} structure to store sounding oscillators per key
     */
		var playingNotes = {}
		var notes = ['A', 'A#', 'B', 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#'];
			for (var num = 0; num<=5; num++){
				for (var letter = 0; letter < notes.length; letter++){
					var note = notes[letter] + String(num);
					var freq = getFrequencyOfNote(note);
					playingNotes[note] = {
									'frequency':freq,
									'oscillators':[],
									4: note_maps[4][note],
									8: note_maps[8][note],
									16: note_maps[16][note]
								};
				}
			}
		return playingNotes;
	};

	this.playingNotes = createPlayingNotesStructure();

	this.createVoice = function(voice){
	/**
     * Create gain/filter flow for {voice}
     * @param {voice} voice object containing properties for gain/filter flow
     * @return {audioNode} connection point to voice
     */
	    var filter = this.context.createBiquadFilter();
		filter.type = voice.filter.type;
		filter.frequency.value = voice.filter.frequency;
		filter.Q.value = voice.filter.Q;

		var outputGain = this.context.createGain()
		outputGain.gain.value = voice.gain;

		filter.connect(outputGain);
		outputGain.connect(this.context.destination);

	    return filter;
	};

	this.playNote = function(note, rocker){
	/**
     * Create and play all oscillators for {rocker} param on {note}
     * @param {note} key pressed
     * @param {rocker} rocker to play
     */
    	var voices = this.rockers[rocker];
    	var vibrato = this.vibrato.gainNode;

     	for(var voice in voices){
     		var key = {};
     		var audibleNote = this.playingNotes[note][voice];
     		var oscillator = this.context.createOscillator();
     		var gainNode = this.context.createGain();

     		oscillator.frequency.value = this.playingNotes[audibleNote].frequency;
     		oscillator.type = this.rockers[rocker][voice]["waveform"];
     		vibrato.connect(oscillator.frequency);

     		oscillator.connect(gainNode);
     		gainNode.connect(this.rockers[rocker][voice]["node"]);

     		oscillator.start(0);
     		key.oscillator = oscillator;
     		key.gainNode = gainNode;
     		key.voiceName = rocker + ':' + voice;
     		this.playingNotes[note]["oscillators"].push(key);
     	}
	}

	this.keyDown = function(note){
	/**
     * Interface for farfisa to start playing a note.
     * Plays notes through all rockers included in 'voiceNodes' structure
     */
		this.playNote(note, "flute4");
		//this.playNote(note, "bass");
		//this.playNote(note, "oboe");
		//this.playNote(note, "trumpet");
		this.playNote(note, "clarinet");
	}

	this.keyUp = function(note){
	/**
     * Interface for farfisa to stop playing all oscillators for note.
     */
    	var keys = this.playingNotes[note].oscillators,
			key, i, now;
		var releaseTime = 1.1;
		for(i=0; keys.length; i++){
			now = this.context.currentTime;
			key = keys.pop();
			key.oscillator.stop(now + releaseTime);

			//create fade out event to avoid key click
			key.gainNode.gain.setValueAtTime(key.gainNode.gain.value, now);
			key.gainNode.gain.exponentialRampToValueAtTime(0.01, now + releaseTime);

			key.oscillator.onended = function(){
			key.oscillator.disconnect();
			key.gainNode.disconnect();
			};
		}
	}
	this.initialize();
};
farfisa.prototype.initialize = function(){
	var rocker, octave, voice;

	for(rocker in this.rockers){
		for(octave in this.rockers[rocker]){
			voice = this.rockers[rocker][octave];
			voice.node = this.createVoice(voice);
		}
	}
	this.vibrato = this.createVibrato();
};
farfisa.prototype.createVibrato = function(){
	var lfo = this.context.createOscillator();
	var gainNode = this.context.createGain();

	lfo.frequency.value = 5;
	lfo.type = 'triangle';
	lfo.start(0);

	gainNode.gain.value = 2.0;

	lfo.connect(gainNode);
	return {lfo, gainNode};

}

<|endoftext|>


<commit_before>
function App() {
	this.login = new Login(this);
	this.currentIndex = -1;

	var color = localStorage.color;
	if(!color) color = "red";
	this.setColor(color);
	this.fontChange();
};

App.prototype.authenticate = function() {

};

App.prototype.after_login = function(backend) {

	var request = window.navigator.mozApps.getSelf();
	request.onsuccess = function() {
		$("#version").innerHTML = request.result.manifest.version;
	}

	var _this = this;

	window.onhashchange = function(e) {

		// do not reload page
		e.preventDefault();
 		e.stopPropagation();

		var url = window.location.hash;

		if(url == "#list") {
			_this.setCurrentRead();
			_this.changeToPage("#list");
		} else if(url == "#reload") {
			_this.reload();
		} else if(url == "#settings") {
			_this.changeToPage("#settings");
		} else if(url.indexOf("#color-") == 0) {
			var color = url.replace("#color-", "");
			_this.setColor(color);
		} else if(url.indexOf("#full-") == 0) {
			var i = parseInt(url.replace("#full-", ""), 10);
			_this.showFull(_this.unread_articles[i]);
		} else if(url == "#unread") {
			_this.toggleCurrentUnread();
		} else if(url == "#starred") {
			_this.toggleStarred();
		} else if(url == "#logout") {
			_this.logout();
		} else if(url == "#reset-info") {
			alert("Info bubbles will be shown again.")
			$$(".info").forEach(function(o) {
				o.removeClass("hidden");
			});
		} else if(url == "#next") {
			_this.showNext();
		} else if(url == "#previous") {
			_this.showPrevious();
		} else if(url == "#font-smaller") {
			_this.fontChange("smaller");
		} else if(url == "#font-bigger") {
			_this.fontChange("bigger");
		} else if(url == "#all-read") {
			_this.toggleAllRead();
		}

		// this is here so you can tap on a button more then once
		// and it will still call onhashchange
		window.location.hash = "#";
	}

	// FIXME move that code somewhere else
	$(".info.swipe").ontouchend = function(e) {
		localStorage.info_swipe = true;
		$(".info.swipe").addClass("hidden");
	};

	var supportsTouch = 'ontouchend' in document;

	if(!supportsTouch || localStorage.info_swipe) {
		$(".info.swipe").addClass("hidden");
	}

	// set up swiping
	jester($("#full")).flick(function(touches, direction) {
		if(direction == "left") _this.showNext();
		else _this.showPrevious();
	});

	this.changeToPage("#list");

	if(backend == "OwnCloud") {
		this.backend = new OwnCloud(this, localStorage.server_url, localStorage.session_id);
	} else {
		this.backend = new TinyTinyRSS(this, localStorage.server_url, localStorage.session_id);
	}
	this.reload();
};

App.prototype.logout = function() {
	this.backend.logOut();
	this.unread_articles = [];
	this.populateList();
	this.login.log_out();
};

App.prototype.changeToPage = function(page) {

	// FIXME
	var active = $(".active");
	if(active.id == "list") {
		this.saveScrollTop = document.body.scrollTop;
	}

	if(page == "#list") {
		document.body.scrollTop = this.saveScrollTop;
	} else {
		window.scroll(0, 0);
	}

	active.removeClass("active");
	$(page).addClass("active");
};

App.prototype.setColor = function(color) {
	localStorage.color = color;
	document.body.className = "";
	document.body.addClass(color);
	this.updatePieChart();
};

App.prototype.reload = function() {
	this.unread_articles = [];
	$("#all-read").innerHTML = "❌";
	this.backend.reload(this.gotUnreadFeeds.bind(this));
};

App.prototype.gotUnreadFeeds = function(new_articles) {

	if(new_articles == null || !this.validate(new_articles)) { // on error load the saved unread articles.

		var old_articles = localStorage.unread_articles;
		if(old_articles) {
			this.unread_articles = JSON.parse(old_articles);
		}
		this.populateList();

	} else {

		this.unread_articles = this.unread_articles.concat(new_articles);

		if(new_articles.length > 0) {
			this.backend.getUnreadFeeds(this.gotUnreadFeeds.bind(this), this.unread_articles);
		} else {
			localStorage.unread_articles = JSON.stringify(this.unread_articles);
			this.populateList();
		}
	}
};

App.prototype.validate = function(articles) {

	if(articles.length == 0) return true;

	for (var i = 0; i < articles.length; i++) {
		if(typeof articles[i].title != "undefined") return true;
	}

	return false;
};

App.prototype.populateList = function() {

	var html_str = "";
	for (var i = 0; i < this.unread_articles.length; i++) {
		var article = this.unread_articles[i];
		html_str += "
";
		html_str += "";
		html_str += "
";
		html_str += "" + article.feed_title + "";
		html_str += "
";
		html_str += "
" + article.title + "
";
		if(article.excerpt)	html_str += "
" + article.excerpt + "
";
		html_str += "
";
	}

	$("#list ul").innerHTML = html_str;

	this.updatePieChart();
};

App.prototype.updateList = function() {
	var unread = 0;
	$$("#list ul li").forEach(function(o, i) {

		if(!this.unread_articles[i].unread) {
			o.removeClass("unread");
		}
		else {
			unread++;
			o.addClass("unread");
		}
	}, this);

	if(unread > 0) {
		$("#all-read").innerHTML = "❌";
	} else {
		$("#all-read").innerHTML = "✓";
	}

	this.updatePieChart();
};

App.prototype.updatePieChart = function() {

	if(!this.unread_articles) return; // happens on loginpage

	var all = this.unread_articles.length;
	var unread = 0;
	for (var i = 0; i < all; i++) {
		if(this.unread_articles[i].unread) unread++;
	};

	var a = 100 / all * unread;
	var b = 100 / all * (all - unread);

	var bg = window.getComputedStyle($("body"), null).backgroundColor;
	var fg = window.getComputedStyle($(".bar"), null).backgroundColor;

	var myColor = [bg, fg];

	var data = [a, b];

	$$("canvas").forEach(function(canvas) {
		var ctx = canvas.getContext("2d");
		var lastend = 0;
		var myTotal = 0;

		for(var e = 0; e < data.length; e++) myTotal += data[e];

		for (var i = 0; i < data.length; i++) {
			ctx.fillStyle = myColor[i];
			ctx.beginPath();
			ctx.moveTo(canvas.width/2, canvas.height/2);
			ctx.arc(canvas.width/2, canvas.height/2, canvas.height/2, lastend, lastend+(Math.PI*2*(data[i]/myTotal)), false);
			ctx.lineTo(canvas.width/2, canvas.height/2);
			ctx.fill();
			lastend += Math.PI*2*(data[i]/myTotal);
		}
	})
};

App.prototype.showFull = function(article, slide_back) {

	this.changeToPage("#full");

	this.currentIndex = this.unread_articles.indexOf(article);

	var page_id = "#full";

	$(page_id + " .date").innerHTML = (new Date(parseInt(article.updated, 10) * 1000)).toLocaleString();

	var title = $(page_id + " .title");
	title.innerHTML = article.title;
	title.href = article.link;

	$(page_id + " .feed_title").innerHTML = article.feed_title;

	$(page_id + " .author").innerHTML = "";
	if(article.author && article.author.length > 0)
		$(page_id + " .author").innerHTML = "– " + article.author;

	$(page_id + " .article").innerHTML = article.content;

	if(article.unread) {
		$("#setunread").innerHTML = "❌";
	} else {
		$("#setunread").innerHTML = "✓";
	}

	if(article.marked) {
		$("#setstarred").innerHTML = "★";
	} else {
		$("#setstarred").innerHTML = "☆";
	}

};

App.prototype.showNext = function() {
	this.setCurrentRead();

	if(this.currentIndex >= this.unread_articles.length - 1) {
		this.goToList();
	} else {
		this.currentIndex++;
		this.showFull(this.unread_articles[this.currentIndex], false);
	}
};

App.prototype.showPrevious = function() {
	this.setCurrentRead();

	if(this.currentIndex <= 0) {
		this.goToList();
	} else {
		this.currentIndex--;
		this.showFull(this.unread_articles[this.currentIndex], true);
	}
};

App.prototype.setCurrentRead = function() {
	var article = this.unread_articles[this.currentIndex];
	if(!article) return; // happens if we're not on a full article site
	if(!article.set_unread) {
		article.unread = false;
		this.updateList();
		this.backend.setArticleRead(article);
	}

	article.set_unread = false;

	$("#setunread").innerHTML = "✓";

	this.updatePieChart();
};

App.prototype.toggleCurrentUnread = function() {
	var article = this.unread_articles[this.currentIndex];
	if(article.unread) {
		article.unread = false;
		article.set_unread = false;
		$("#setunread").innerHTML = "✓";
	} else {
		article.unread = true;
		article.set_unread = true;
		$("#setunread").innerHTML = "❌";
	}

	this.updateList();
	this.backend.setArticleUnread(article);
};

App.prototype.toggleAllRead = function() {

	if($("#all-read").innerHTML == "❌") { // set all read

		var articles = [];
		for (var i = 0; i < this.unread_articles.length; i++) {
			var article = this.unread_articles[i];
			article.unread = false;
			article.set_unread = false;
			articles.push(article);
		}
		$("#all-read").innerHTML = "✓";

		this.updateList();

		this.backend.setArticlesRead(articles);

	} else {

		var articles = [];
		for (var i = 0; i < this.unread_articles.length; i++) {
			var article = this.unread_articles[i];
			article.unread = true;
			article.set_unread = false;
			articles.push(article);
		}
		$("#all-read").innerHTML = "❌";
		this.updateList();

		this.backend.setArticlesUnread(articles);

	}
};

App.prototype.toggleStarred = function() {
	var article = this.unread_articles[this.currentIndex];
	if(!article) return; // happens if we're not on a full article site

	if(!article.marked) {
		article.marked = true;
		this.updateList();
		this.backend.setArticleStarred(article);
		$("#setstarred").innerHTML = "★";
	}
	else {
		article.marked = false;
		this.updateList();
		this.backend.setArticleUnstarred(article);
		$("#setstarred").innerHTML = "☆";
	}

};

App.prototype.goToList = function() {
	this.changeToPage("#list");
};

App.prototype.fontChange = function(size) {
	if(size == "bigger") {

		var i = localStorage.font_size;
		if(i < 5) {
			document.body.removeClass("f" + i);
			i++;
			document.body.addClass("f" + i);
			localStorage.font_size = i;
		}

	} else if(size == "smaller") {

		var i = localStorage.font_size;
		if(i > 1) {
			document.body.removeClass("f" + i);
			i--;
			document.body.addClass("f" + i);
			localStorage.font_size = i;
		}

	} else {

		var i = localStorage.font_size;
		if(typeof i == "undefined") {
			i = localStorage.font_size = 2;
		}

		document.body.addClass("f" + i);
	}

};
<commit_msg>remove unsemantic strong tag from feed title<commit_after>
function App() {
	this.login = new Login(this);
	this.currentIndex = -1;

	var color = localStorage.color;
	if(!color) color = "red";
	this.setColor(color);
	this.fontChange();
};

App.prototype.authenticate = function() {

};

App.prototype.after_login = function(backend) {

	var request = window.navigator.mozApps.getSelf();
	request.onsuccess = function() {
		$("#version").innerHTML = request.result.manifest.version;
	}

	var _this = this;

	window.onhashchange = function(e) {

		// do not reload page
		e.preventDefault();
 		e.stopPropagation();

		var url = window.location.hash;

		if(url == "#list") {
			_this.setCurrentRead();
			_this.changeToPage("#list");
		} else if(url == "#reload") {
			_this.reload();
		} else if(url == "#settings") {
			_this.changeToPage("#settings");
		} else if(url.indexOf("#color-") == 0) {
			var color = url.replace("#color-", "");
			_this.setColor(color);
		} else if(url.indexOf("#full-") == 0) {
			var i = parseInt(url.replace("#full-", ""), 10);
			_this.showFull(_this.unread_articles[i]);
		} else if(url == "#unread") {
			_this.toggleCurrentUnread();
		} else if(url == "#starred") {
			_this.toggleStarred();
		} else if(url == "#logout") {
			_this.logout();
		} else if(url == "#reset-info") {
			alert("Info bubbles will be shown again.")
			$$(".info").forEach(function(o) {
				o.removeClass("hidden");
			});
		} else if(url == "#next") {
			_this.showNext();
		} else if(url == "#previous") {
			_this.showPrevious();
		} else if(url == "#font-smaller") {
			_this.fontChange("smaller");
		} else if(url == "#font-bigger") {
			_this.fontChange("bigger");
		} else if(url == "#all-read") {
			_this.toggleAllRead();
		}

		// this is here so you can tap on a button more then once
		// and it will still call onhashchange
		window.location.hash = "#";
	}

	// FIXME move that code somewhere else
	$(".info.swipe").ontouchend = function(e) {
		localStorage.info_swipe = true;
		$(".info.swipe").addClass("hidden");
	};

	var supportsTouch = 'ontouchend' in document;

	if(!supportsTouch || localStorage.info_swipe) {
		$(".info.swipe").addClass("hidden");
	}

	// set up swiping
	jester($("#full")).flick(function(touches, direction) {
		if(direction == "left") _this.showNext();
		else _this.showPrevious();
	});

	this.changeToPage("#list");

	if(backend == "OwnCloud") {
		this.backend = new OwnCloud(this, localStorage.server_url, localStorage.session_id);
	} else {
		this.backend = new TinyTinyRSS(this, localStorage.server_url, localStorage.session_id);
	}
	this.reload();
};

App.prototype.logout = function() {
	this.backend.logOut();
	this.unread_articles = [];
	this.populateList();
	this.login.log_out();
};

App.prototype.changeToPage = function(page) {

	// FIXME
	var active = $(".active");
	if(active.id == "list") {
		this.saveScrollTop = document.body.scrollTop;
	}

	if(page == "#list") {
		document.body.scrollTop = this.saveScrollTop;
	} else {
		window.scroll(0, 0);
	}

	active.removeClass("active");
	$(page).addClass("active");
};

App.prototype.setColor = function(color) {
	localStorage.color = color;
	document.body.className = "";
	document.body.addClass(color);
	this.updatePieChart();
};

App.prototype.reload = function() {
	this.unread_articles = [];
	$("#all-read").innerHTML = "❌";
	this.backend.reload(this.gotUnreadFeeds.bind(this));
};

App.prototype.gotUnreadFeeds = function(new_articles) {

	if(new_articles == null || !this.validate(new_articles)) { // on error load the saved unread articles.

		var old_articles = localStorage.unread_articles;
		if(old_articles) {
			this.unread_articles = JSON.parse(old_articles);
		}
		this.populateList();

	} else {

		this.unread_articles = this.unread_articles.concat(new_articles);

		if(new_articles.length > 0) {
			this.backend.getUnreadFeeds(this.gotUnreadFeeds.bind(this), this.unread_articles);
		} else {
			localStorage.unread_articles = JSON.stringify(this.unread_articles);
			this.populateList();
		}
	}
};

App.prototype.validate = function(articles) {

	if(articles.length == 0) return true;

	for (var i = 0; i < articles.length; i++) {
		if(typeof articles[i].title != "undefined") return true;
	}

	return false;
};

App.prototype.populateList = function() {

	var html_str = "";
	for (var i = 0; i < this.unread_articles.length; i++) {
		var article = this.unread_articles[i];
		html_str += "
";
		html_str += "";
		html_str += "
" + article.feed_title + "
";
		html_str += "
" + article.title + "
";
		if(article.excerpt)	html_str += "
" + article.excerpt + "
";
		html_str += "
";
	}

	$("#list ul").innerHTML = html_str;

	this.updatePieChart();
};

App.prototype.updateList = function() {
	var unread = 0;
	$$("#list ul li").forEach(function(o, i) {

		if(!this.unread_articles[i].unread) {
			o.removeClass("unread");
		}
		else {
			unread++;
			o.addClass("unread");
		}
	}, this);

	if(unread > 0) {
		$("#all-read").innerHTML = "❌";
	} else {
		$("#all-read").innerHTML = "✓";
	}

	this.updatePieChart();
};

App.prototype.updatePieChart = function() {

	if(!this.unread_articles) return; // happens on loginpage

	var all = this.unread_articles.length;
	var unread = 0;
	for (var i = 0; i < all; i++) {
		if(this.unread_articles[i].unread) unread++;
	};

	var a = 100 / all * unread;
	var b = 100 / all * (all - unread);

	var bg = window.getComputedStyle($("body"), null).backgroundColor;
	var fg = window.getComputedStyle($(".bar"), null).backgroundColor;

	var myColor = [bg, fg];

	var data = [a, b];

	$$("canvas").forEach(function(canvas) {
		var ctx = canvas.getContext("2d");
		var lastend = 0;
		var myTotal = 0;

		for(var e = 0; e < data.length; e++) myTotal += data[e];

		for (var i = 0; i < data.length; i++) {
			ctx.fillStyle = myColor[i];
			ctx.beginPath();
			ctx.moveTo(canvas.width/2, canvas.height/2);
			ctx.arc(canvas.width/2, canvas.height/2, canvas.height/2, lastend, lastend+(Math.PI*2*(data[i]/myTotal)), false);
			ctx.lineTo(canvas.width/2, canvas.height/2);
			ctx.fill();
			lastend += Math.PI*2*(data[i]/myTotal);
		}
	})
};

App.prototype.showFull = function(article, slide_back) {

	this.changeToPage("#full");

	this.currentIndex = this.unread_articles.indexOf(article);

	var page_id = "#full";

	$(page_id + " .date").innerHTML = (new Date(parseInt(article.updated, 10) * 1000)).toLocaleString();

	var title = $(page_id + " .title");
	title.innerHTML = article.title;
	title.href = article.link;

	$(page_id + " .feed_title").innerHTML = article.feed_title;

	$(page_id + " .author").innerHTML = "";
	if(article.author && article.author.length > 0)
		$(page_id + " .author").innerHTML = "– " + article.author;

	$(page_id + " .article").innerHTML = article.content;

	if(article.unread) {
		$("#setunread").innerHTML = "❌";
	} else {
		$("#setunread").innerHTML = "✓";
	}

	if(article.marked) {
		$("#setstarred").innerHTML = "★";
	} else {
		$("#setstarred").innerHTML = "☆";
	}

};

App.prototype.showNext = function() {
	this.setCurrentRead();

	if(this.currentIndex >= this.unread_articles.length - 1) {
		this.goToList();
	} else {
		this.currentIndex++;
		this.showFull(this.unread_articles[this.currentIndex], false);
	}
};

App.prototype.showPrevious = function() {
	this.setCurrentRead();

	if(this.currentIndex <= 0) {
		this.goToList();
	} else {
		this.currentIndex--;
		this.showFull(this.unread_articles[this.currentIndex], true);
	}
};

App.prototype.setCurrentRead = function() {
	var article = this.unread_articles[this.currentIndex];
	if(!article) return; // happens if we're not on a full article site
	if(!article.set_unread) {
		article.unread = false;
		this.updateList();
		this.backend.setArticleRead(article);
	}

	article.set_unread = false;

	$("#setunread").innerHTML = "✓";

	this.updatePieChart();
};

App.prototype.toggleCurrentUnread = function() {
	var article = this.unread_articles[this.currentIndex];
	if(article.unread) {
		article.unread = false;
		article.set_unread = false;
		$("#setunread").innerHTML = "✓";
	} else {
		article.unread = true;
		article.set_unread = true;
		$("#setunread").innerHTML = "❌";
	}

	this.updateList();
	this.backend.setArticleUnread(article);
};

App.prototype.toggleAllRead = function() {

	if($("#all-read").innerHTML == "❌") { // set all read

		var articles = [];
		for (var i = 0; i < this.unread_articles.length; i++) {
			var article = this.unread_articles[i];
			article.unread = false;
			article.set_unread = false;
			articles.push(article);
		}
		$("#all-read").innerHTML = "✓";

		this.updateList();

		this.backend.setArticlesRead(articles);

	} else {

		var articles = [];
		for (var i = 0; i < this.unread_articles.length; i++) {
			var article = this.unread_articles[i];
			article.unread = true;
			article.set_unread = false;
			articles.push(article);
		}
		$("#all-read").innerHTML = "❌";
		this.updateList();

		this.backend.setArticlesUnread(articles);

	}
};

App.prototype.toggleStarred = function() {
	var article = this.unread_articles[this.currentIndex];
	if(!article) return; // happens if we're not on a full article site

	if(!article.marked) {
		article.marked = true;
		this.updateList();
		this.backend.setArticleStarred(article);
		$("#setstarred").innerHTML = "★";
	}
	else {
		article.marked = false;
		this.updateList();
		this.backend.setArticleUnstarred(article);
		$("#setstarred").innerHTML = "☆";
	}

};

App.prototype.goToList = function() {
	this.changeToPage("#list");
};

App.prototype.fontChange = function(size) {
	if(size == "bigger") {

		var i = localStorage.font_size;
		if(i < 5) {
			document.body.removeClass("f" + i);
			i++;
			document.body.addClass("f" + i);
			localStorage.font_size = i;
		}

	} else if(size == "smaller") {

		var i = localStorage.font_size;
		if(i > 1) {
			document.body.removeClass("f" + i);
			i--;
			document.body.addClass("f" + i);
			localStorage.font_size = i;
		}

	} else {

		var i = localStorage.font_size;
		if(typeof i == "undefined") {
			i = localStorage.font_size = 2;
		}

		document.body.addClass("f" + i);
	}

};
<|endoftext|>


<commit_before>var vscode = require('vscode');
var fs = require('fs');
var request = require('request');
var extract = require('extract-zip');
var path = require('path');
var replace = require('replace-in-file');

function activate(context) {

    console.log('Congratulations, your extension is now active!');

    var isWin = /^win/.test(process.platform);
    var appDir = path.dirname(require.main.filename);

    var base = appDir + (isWin ? '\vs\workbench' : '/vs/workbench');
    var iconFolder = base + (isWin ? '\parts\files\browser\media' : '/parts/files/browser/media');

    var cssfile = base + (isWin ? '\workbench.main.css' : '/workbench.main.css');
    var cssfilebak = base + (isWin ? '\workbench.main.css.bak' : '/workbench.main.css.bak');

    var cssreplace = '/*! *****************************************************************************';
    var csswith = '.monaco-tree-row .content .sub-content .explorer-item.folder-icon{padding-left:22px;background:url(parts/files/browser/media/icons/Folder_inverse.svg) 1px 4px no-repeat}.monaco-tree-row.expanded .content .sub-content .explorer-item.folder-icon{padding-left:22px;background:url(parts/files/browser/media/icons/Folder_opened.svg) 1px 4px no-repeat;background-size:16px}.explorer-item.text-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/File.svg) 1px 3px no-repeat;background-size:16px}.explorer-item.js-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_js@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.json-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_node@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.jsx-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_react@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.css-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_css@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.scss-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_sass@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.htm-file-icon,.explorer-item.html-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_html@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.bmp-file-icon,.explorer-item.gif-file-icon,.explorer-item.jpeg-file-icon,.explorer-item.jpg-file-icon,.explorer-item.png-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_image@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.svg-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_svg@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.sql-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_sql@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.md-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_markdown@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.yml-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_yaml@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.ts-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_ts@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.dockerfile-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_docker@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.gitattributes-file-icon,.explorer-item.gitignore-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_git@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.otf-file-icon,.explorer-item.ttf-file-icon,.explorer-item.woff-file-icon,.explorer-item.woff2-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_font@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.less-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_less@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.jade-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_jade@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.php-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_php@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.txt-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_txt@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.license-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_license@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.bat-file-icon,.explorer-item.cmd-file-icon,.explorer-item.sh-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_shell@2x.png) 1px 4px no-repeat;background-size:16px} ';

    function replaceCss() {
        replace({
            files: cssfile,
            replace: cssreplace,
            with: csswith + cssreplace
        }, function (err, changedFiles) {
            console.log(err);
        });
    }

    var jsfile = base + (isWin ? '\workbench.main.js' : '/workbench.main.js');
    var jsfilebak = base + (isWin ? '\workbench.main.js.bak' : '/workbench.main.js.bak');

    var jsreplace = 't.prototype.iconClass=function(e){return e.isDirectory?"folder-icon":"text-file-icon"}';
    var jswith = 't.prototype.iconClass=function(s){if(s.isDirectory)return"folder-icon";var e=s.name.substring(s.name.lastIndexOf(".")+1).toLowerCase();switch(e){case"license":case"cmd":case"bat":case"sh":case"txt":case"php":case"jade":case"ttf":case"otf":case"woff2":case"woff":case"gitattributes":case"gitignore":case"svg":case"gif":case"png":case"sql":case"less":case"dockerfile":case"yml":case"ts":case"jpg":case"js":case"jsx":case"css":case"scss":case"md":case"json":case"html":case"htm":return e+"-file-icon";default:return"text-file-icon"}}';

    function replaceJs() {
        replace({
            files: jsfile,
            replace: jsreplace,
            with: jswith
        }, function (err, changedFiles) {
            console.log(err);
        });
    }

    var installIcons = vscode.commands.registerCommand('extension.installIcons', function () {

        fs.access(cssfilebak, function (err) {
            if (err) {
                var c = fs.createReadStream(cssfile).pipe(fs.createWriteStream(cssfilebak));
                c.on('finish', function () {
                    replaceCss();
                });
            } else {
                replaceCss();
            }
        });

        fs.access(jsfilebak, function (err) {
            if (err) {
                var j = fs.createReadStream(jsfile).pipe(fs.createWriteStream(jsfilebak));
                j.on('finish', function () {
                    replaceJs();
                });
            } else {
                replaceJs();
            }
        });

        //add icons to folder
        var zipUrl = 'http://github.com/robertohuertasm/vscode-icons/blob/master/icons.zip?raw=true';
        k = iconFolder + (isWin ? '\icons.zip' : '/icons.zip');

        var r = request(zipUrl).pipe(fs.createWriteStream(k));
        r.on('finish', function () {
            extract(k, { dir: iconFolder }, function (err) {
                // extraction is complete. make sure to handle the err
                if (err) console.log(err);
                vscode.window.showInformationMessage('Icons enabled. Restart the IDE.');
                //remove icon.zip
                fs.unlink(k, function (err) {
                    console.log(err)
                })
            });
        });

    });

    var uninstallIcons = vscode.commands.registerCommand('extension.uninstallIcons', function () {

        fs.access(jsfilebak, function (err) {
            if (err) {
                vscode.window.showInformationMessage('Icons already disabled.');
                return;
            }
            fs.unlink(jsfile, function (err) {
                var j = fs.createReadStream(jsfilebak).pipe(fs.createWriteStream(jsfile));
                j.on('finish', function () {
                    fs.unlink(jsfilebak);
                });
            });
            fs.unlink(cssfile, function (err) {
                var c = fs.createReadStream(cssfilebak).pipe(fs.createWriteStream(cssfile));
                c.on('finish', function () {
                    fs.unlink(cssfilebak);
                });
            })

            vscode.window.showInformationMessage('Icons disabled. Restart the IDE.');
        });

    });

    context.subscriptions.push(uninstallIcons);
}
exports.activate = activate;

// this method is called when your extension is deactivated
function deactivate() {
}
exports.deactivate = deactivate;<commit_msg>windows paths<commit_after>var vscode = require('vscode');
var fs = require('fs');
var request = require('request');
var extract = require('extract-zip');
var path = require('path');
var replace = require('replace-in-file');

function activate(context) {

    console.log('Congratulations, your extension is now active!');

    var isWin = /^win/.test(process.platform);
    var appDir = path.dirname(require.main.filename);

    var base = appDir + (isWin ? '\\vs\\workbench' : '/vs/workbench');
    var iconFolder = base + (isWin ? '\\parts\\files\\browser\\media' : '/parts/files/browser/media');

    var cssfile = base + (isWin ? '\\workbench.main.css' : '/workbench.main.css');
    var cssfilebak = base + (isWin ? '\\workbench.main.css.bak' : '/workbench.main.css.bak');

    var cssreplace = '/*! *****************************************************************************';
    var csswith = '.monaco-tree-row .content .sub-content .explorer-item.folder-icon{padding-left:22px;background:url(parts/files/browser/media/icons/Folder_inverse.svg) 1px 4px no-repeat}.monaco-tree-row.expanded .content .sub-content .explorer-item.folder-icon{padding-left:22px;background:url(parts/files/browser/media/icons/Folder_opened.svg) 1px 4px no-repeat;background-size:16px}.explorer-item.text-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/File.svg) 1px 3px no-repeat;background-size:16px}.explorer-item.js-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_js@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.json-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_node@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.jsx-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_react@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.css-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_css@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.scss-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_sass@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.htm-file-icon,.explorer-item.html-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_html@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.bmp-file-icon,.explorer-item.gif-file-icon,.explorer-item.jpeg-file-icon,.explorer-item.jpg-file-icon,.explorer-item.png-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_image@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.svg-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_svg@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.sql-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_sql@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.md-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_markdown@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.yml-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_yaml@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.ts-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_ts@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.dockerfile-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_docker@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.gitattributes-file-icon,.explorer-item.gitignore-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_git@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.otf-file-icon,.explorer-item.ttf-file-icon,.explorer-item.woff-file-icon,.explorer-item.woff2-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_font@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.less-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_less@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.jade-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_jade@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.php-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_php@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.txt-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_txt@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.license-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_license@2x.png) 1px 4px no-repeat;background-size:16px}.explorer-item.bat-file-icon,.explorer-item.cmd-file-icon,.explorer-item.sh-file-icon{padding-left:22px;background:url(parts/files/browser/media/icons/file_type_shell@2x.png) 1px 4px no-repeat;background-size:16px} ';

    function replaceCss() {
        replace({
            files: cssfile,
            replace: cssreplace,
            with: csswith + cssreplace
        }, function (err, changedFiles) {
            console.log(err);
        });
    }

    var jsfile = base + (isWin ? '\\workbench.main.js' : '/workbench.main.js');
    var jsfilebak = base + (isWin ? '\\workbench.main.js.bak' : '/workbench.main.js.bak');

    var jsreplace = 't.prototype.iconClass=function(e){return e.isDirectory?"folder-icon":"text-file-icon"}';
    var jswith = 't.prototype.iconClass=function(s){if(s.isDirectory)return"folder-icon";var e=s.name.substring(s.name.lastIndexOf(".")+1).toLowerCase();switch(e){case"license":case"cmd":case"bat":case"sh":case"txt":case"php":case"jade":case"ttf":case"otf":case"woff2":case"woff":case"gitattributes":case"gitignore":case"svg":case"gif":case"png":case"sql":case"less":case"dockerfile":case"yml":case"ts":case"jpg":case"js":case"jsx":case"css":case"scss":case"md":case"json":case"html":case"htm":return e+"-file-icon";default:return"text-file-icon"}}';

    function replaceJs() {
        replace({
            files: jsfile,
            replace: jsreplace,
            with: jswith
        }, function (err, changedFiles) {
            console.log(err);
        });
    }

    var installIcons = vscode.commands.registerCommand('extension.installIcons', function () {

        fs.access(cssfilebak, function (err) {
            if (err) {
                var c = fs.createReadStream(cssfile).pipe(fs.createWriteStream(cssfilebak));
                c.on('finish', function () {
                    replaceCss();
                });
            } else {
                replaceCss();
            }
        });

        fs.access(jsfilebak, function (err) {
            if (err) {
                var j = fs.createReadStream(jsfile).pipe(fs.createWriteStream(jsfilebak));
                j.on('finish', function () {
                    replaceJs();
                });
            } else {
                replaceJs();
            }
        });

        //add icons to folder
        var zipUrl = 'http://github.com/robertohuertasm/vscode-icons/blob/master/icons.zip?raw=true';
        k = iconFolder + (isWin ? '\\icons.zip' : '/icons.zip');

        var r = request(zipUrl).pipe(fs.createWriteStream(k));
        r.on('finish', function () {
            extract(k, { dir: iconFolder }, function (err) {
                // extraction is complete. make sure to handle the err
                if (err) console.log(err);
                vscode.window.showInformationMessage('Icons enabled. Restart the IDE.');
                //remove icon.zip
                fs.unlink(k, function (err) {
                    console.log(err)
                })
            });
        });

    });

    var uninstallIcons = vscode.commands.registerCommand('extension.uninstallIcons', function () {

        fs.access(jsfilebak, function (err) {
            if (err) {
                vscode.window.showInformationMessage('Icons already disabled.');
                return;
            }
            fs.unlink(jsfile, function (err) {
                var j = fs.createReadStream(jsfilebak).pipe(fs.createWriteStream(jsfile));
                j.on('finish', function () {
                    fs.unlink(jsfilebak);
                });
            });
            fs.unlink(cssfile, function (err) {
                var c = fs.createReadStream(cssfilebak).pipe(fs.createWriteStream(cssfile));
                c.on('finish', function () {
                    fs.unlink(cssfilebak);
                });
            })

            vscode.window.showInformationMessage('Icons disabled. Restart the IDE.');
        });

    });

    context.subscriptions.push(uninstallIcons);
}
exports.activate = activate;

// this method is called when your extension is deactivated
function deactivate() {
}
exports.deactivate = deactivate;<|endoftext|>