<commit_before>var app = {
    planGroup: null,
    // Application Constructor
    initialize: function() {
        this.bindEvents();
    },
    bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
    },
    renderData: function() {
        // this.responseText is the file content
        var plans = [];
        try {
            plans = JSON.parse(this.responseText);
        } catch(e) {
            console.log('DEBUG: Unable to parse the JSON file');
        }
        app.createUI(plans);
    },
    createUI: function(plans) {
        // using day of the week in current language
        navigator.globalization.getDateNames(function(dayOfWeek){
            navigator.globalization.getFirstDayOfWeek(function(firstDay){
                app.drawUI(plans, dayOfWeek, firstDay);
            });
        }, function() {}, {type: 'narrow', item: 'days'});
    },
    drawUI: function(plans, dayOfWeek, firstDay) {
        var deck = document.getElementById('plan-group');
        var tabbar = document.getElementById('plan-group-menu');
        // calculate the shift based on first day
        var dayShift = (1 - firstDay.value) % 7;

        // XXX: there is a possibility of race condition, a simple hack is
        // to select the tab after a timeout.
        function selectTab(activeTab) {
            function selectActiveTab() {
                activeTab.select();
            }
            window.setTimeout(selectActiveTab, 100);
        }

        for (var i = 0; i < plans.length; i++) {
            var plan = plans[i];
            var numberOfDays = plan.week.length;
            // create plan table
            var table = document.createElement('table');
            // create table header
            var thead = document.createElement('thead');
            var th_empty = document.createElement('th');
            thead.appendChild(th_empty);
            for (var j = 0; j < numberOfDays; j++) {
                // add th only if week isn't empty
                if (plan.week[j].length > 0) {
                    var th = document.createElement('th');
                    th.appendChild(document.createTextNode(dayOfWeek.value[j + dayShift]));
                    thead.appendChild(th);
                }
            }
            table.appendChild(thead);
            // create table body
            //
            // transpone table (tables are created per hour instead
            // of per day as data is written)
            var daysInHours = [];
            // delete non existing days

            var cleanPlan = [];
            for (j = 0; j < numberOfDays; j++) {
                if (plan.week[j].length > 0) {
                    cleanPlan.push(plan.week[j]);
                }
            }
            for (j = 0; j < cleanPlan.length; j++) {
                for (var k = 0; k < cleanPlan[j].length; k++) {
                    if (!daysInHours[k]) {
                        daysInHours[k] = [];
                    }
                    daysInHours[k][j] = cleanPlan[j][k];
                }
            }
            var tbody = document.createElement('tbody');

            //create tab
            var tab = document.createElement('brick-tabbar-tab');
            tab.setAttribute('target', plan.id);
            tab.appendChild(document.createTextNode(plan.title));
            tabbar.appendChild(tab);

            // create card
            var card = document.createElement('brick-card');
            card.setAttribute('id', plan.id);
            deck.appendChild(card);

            // Switching from one tab to another is done automatically
            // We just need to link it backwards - change tab if
            // card is changed without touching the tabbar elements
            card.tabElement = tab;
            card.addEventListener('show', function() {
                this.tabElement.select();
            });

            // create content of the card
            for (var j = 0; j < daysInHours.length; j++) {
                var tr = document.createElement('tr');
                var td = document.createElement('td');
                tr.appendChild(td);
                td.appendChild(document.createTextNode(j + 1));
                // we use cleanPlan.length here as we want all hours to
                // be rendered in all days
                for (var k = 0; k < cleanPlan.length; k++) {
                    var td = document.createElement('td');
                    if (daysInHours[j][k]) {
                        td.appendChild(document.createTextNode(daysInHours[j][k]));
                    }
                    tr.appendChild(td);
                }
                tbody.appendChild(tr);
            }
            table.appendChild(tbody);
            card.appendChild(table);

            // select the active tab
            if (plan.active) {
                selectTab(tab);
            }
        }
    },
    onDeviceReady: function() {
        // Load plans from JSON
        // Get access to data/plans.json file
        var request = new XMLHttpRequest();
        request.onload = app.renderData;
        request.onerror = function(error) {
            console.log('DEBUG: Failed to get ``app_data/plans.json`` file', error);
        };
        request.open("get", "app_data/plans.json", true);
        request.send();

        // Implementing one finger swipe to change deck card
        app.planGroup = document.getElementById('plan-group');

        var startX = null;
        var slideThreshold = 100;

        function touchStart(sX) {
            startX = sX;
        }

        function touchEnd(endX) {
            var deltaX;
            if (startX) {
                deltaX = endX - startX;
                if (Math.abs(deltaX) > slideThreshold) {
                    startX = null;
                    if (deltaX > 0) {
                        app.previousPlan();
                    } else {
                        app.nextPlan();
                    }
                }
            }
        }

        app.planGroup.addEventListener('touchstart', function(evt) {
            var touches = evt.changedTouches;
            if (touches.length == 1) {
                // only one finger swipe is important for us
                touchStart(touches[0].pageX);
            }
        });

        app.planGroup.addEventListener('touchmove', function(evt) {
            // switched off scrolling on webkit
            evt.preventDefault();
            touchEnd(evt.changedTouches[0].pageX);
        });
    },
    previousPlan: function() {
        app.planGroup.previousCard();
    },
    nextPlan: function() {
        app.planGroup.nextCard();
    }
}

window.onload = function() {
  app.initialize();
}
<commit_msg>use table API use polling and select card instead of tab<commit_after>var app = {
    planGroup: null,
    // Application Constructor
    initialize: function() {
        this.bindEvents();
    },
    bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
    },
    renderData: function() {
        // this.responseText is the file content
        var plans = [];
        try {
            plans = JSON.parse(this.responseText);
        } catch(e) {
            console.log('DEBUG: Unable to parse the JSON file');
        }
        app.createUI(plans);
    },
    createUI: function(plans) {
        // using day of the week in current language
        navigator.globalization.getDateNames(function(dayOfWeek){
            navigator.globalization.getFirstDayOfWeek(function(firstDay){
                app.drawUI(plans, dayOfWeek, firstDay);
            });
        }, function() {}, {type: 'narrow', item: 'days'});
    },
    drawUI: function(plans, dayOfWeek, firstDay) {
        var deck = document.getElementById('plan-group');
        var tabbar = document.getElementById('plan-group-menu');
        // calculate the shift based on first day
        var dayShift = (1 - firstDay.value) % 7;

        // there is a possibility of race condition, a simple hack is
        // to use polling.
        function selectTab(activeTab) {
            function selectActiveTab() {
                if (!activeTab.targetElement) {
                    return window.setTimeout(selectActiveTab, 100);
                }
                deck.showCard(activeTab.targetElement);
            }
            selectActiveTab();
        }

        for (var i = 0; i < plans.length; i++) {
            var plan = plans[i];

            //create tab
            var tab = document.createElement('brick-tabbar-tab');
            tab.setAttribute('target', plan.id);
            tab.appendChild(document.createTextNode(plan.title));
            tabbar.appendChild(tab);

            // create card
            var card = document.createElement('brick-card');
            card.setAttribute('id', plan.id);
            deck.appendChild(card);

            // create plan table
            var table = document.createElement('table');

            // transpone table (tables are created per hour instead
            // of per day as data is written)
            var numberOfDays = plan.week.length;
            var daysInHours = [];
            // delete non existing days

            var cleanPlan = [];
            for (j = 0; j < numberOfDays; j++) {
                if (plan.week[j].length > 0) {
                    cleanPlan.push(plan.week[j]);
                }
            }
            for (j = 0; j < cleanPlan.length; j++) {
                for (var k = 0; k < cleanPlan[j].length; k++) {
                    if (!daysInHours[k]) {
                        daysInHours[k] = [];
                    }
                    daysInHours[k][j] = cleanPlan[j][k];
                }
            }

            // Switching from one tab to another is done automatically
            // We just need to link it backwards - change tab if
            // card is changed without touching the tabbar elements
            card.tabElement = tab;
            card.addEventListener('show', function() {
                this.tabElement.select();
            });

            // create content of the card
            for (var j = 0; j < daysInHours.length; j++) {
                var tr = table.insertRow(-1);
                var td = tr.insertCell();
                td.appendChild(document.createTextNode(j + 1));
                // we use cleanPlan.length here as we want all hours to
                // be rendered in all days
                for (var k = 0; k < cleanPlan.length; k++) {
                    var td = tr.insertCell();
                    if (daysInHours[j][k]) {
                        td.appendChild(document.createTextNode(daysInHours[j][k]));
                    }
                }
            }
            // create table header
            var thead = table.createTHead();
            var tr = thead.insertRow();
            var th_empty = document.createElement('th');
            tr.appendChild(th_empty);
            for (var j = 0; j < numberOfDays; j++) {
                // add th only if week isn't empty
                if (plan.week[j].length > 0) {
                    var th = document.createElement('th');
                    th.appendChild(document.createTextNode(dayOfWeek.value[j + dayShift]));
                    tr.appendChild(th);
                }
            }
            card.appendChild(table);

            // select the active tab
            if (plan.active) {
                selectTab(tab);
            }
        }
    },
    onDeviceReady: function() {
        // Load plans from JSON
        // Get access to data/plans.json file
        var request = new XMLHttpRequest();
        request.onload = app.renderData;
        request.onerror = function(error) {
            console.log('DEBUG: Failed to get ``app_data/plans.json`` file', error);
        };
        request.open("get", "app_data/plans.json", true);
        request.send();

        // Implementing one finger swipe to change deck card
        app.planGroup = document.getElementById('plan-group');

        var startX = null;
        var slideThreshold = 100;

        function touchStart(sX) {
            startX = sX;
        }

        function touchEnd(endX) {
            var deltaX;
            if (startX) {
                deltaX = endX - startX;
                if (Math.abs(deltaX) > slideThreshold) {
                    startX = null;
                    if (deltaX > 0) {
                        app.previousPlan();
                    } else {
                        app.nextPlan();
                    }
                }
            }
        }

        app.planGroup.addEventListener('touchstart', function(evt) {
            var touches = evt.changedTouches;
            if (touches.length == 1) {
                // only one finger swipe is important for us
                touchStart(touches[0].pageX);
            }
        });

        app.planGroup.addEventListener('touchmove', function(evt) {
            // switched off scrolling on webkit
            evt.preventDefault();
            touchEnd(evt.changedTouches[0].pageX);
        });
    },
    previousPlan: function() {
        app.planGroup.previousCard();
    },
    nextPlan: function() {
        app.planGroup.nextCard();
    }
}

window.onload = function() {
  app.initialize();
}
<|endoftext|>


<commit_before>var config = require('./config.js');
var ip = config.ip;
var port = config.port;
var express = require('express');
var app = express();
var server = app.listen(port);
var ejs = require('ejs');
var bbcode = require('bbcode');
var io = require('socket.io').listen(server);
io.set('log level', 1);
var sys = require('sys')
var exec = require('child_process').exec;

app.set("view options", {layout: false});
app.use(express.json());
app.use(express.static(__dirname + '/public'));

process.on('SIGINT', function() {
  console.log( "\nShutting down from manual SIGINT (Ctrl-C), NOW." );
  io.sockets.emit('annouce', {message : 'SHUTTING DOWN, NOW!'});
  process.exit();
});

process.once('SIGUSR2', function () {
  gracefulShutdown(function () {
    process.kill(process.pid, 'SIGUSR2');
  });
});

function gracefulShutdown(kill){
  console.log( "\nShutting down from nodemon SIGUSR2 (RESTART) in 10 seconds..." );
  io.sockets.emit('annouce', {message : 'RESTARTING IN 10 SECONDS!'});
  setTimeout(function(){kill()}, 10000);
}

app.post('/gitpull', function(req, res) {
  //GitHub push hook
  res.end();
  io.sockets.emit('annouce', {message : 'SYSTEM UPDATE INITIATED...'});
  function puts(error, stdout, stderr) {sys.puts(stdout)}
  exec("git reset --hard HEAD", puts);
  exec("git pull", puts);
  var arr = req.body.commits[0].modified;
  if((arr.join(',').indexOf("app.min.js") > -1) || (arr.join(',').indexOf("app.min.css") > -1) || (arr.join(',').indexOf("index.ejs") > -1)){
    io.sockets.emit('annouce', {message : 'SYSTEM UPDATE COMPLETE, BROWSER RELOAD IS NECCESARY.'});
  } else {
    io.sockets.emit('annouce', {message : 'SYSTEM UPDATE COMPLETE, BROWSER RELOAD IS NOT NECCESARY.'});
  }
});

app.get('*', function(req, res) {
  res.render('index.ejs', {
   ip: ip
 });
});

var users = [];

process.stdin.resume();
process.stdin.setEncoding('utf8');

process.stdin.on('data', function (chunk) {
 io.sockets.emit('annouce', {message : '' + chunk.toUpperCase() + ''});
});

io.sockets.on('connection', function (socket) {
  var name = 'Guest' + Math.floor(100 + Math.random() * 900);

  function broadcast(data) {
    io.sockets.emit('annouce', {message : data});
  }

  function parseServerCommand(data) {
    var res = data.message.trim().split(" ");

    if(res[0].toLowerCase() == "/nick"){
      updateName(res.slice(1,res.length).join(' '));
      return;
    }

    if(res[0].toLowerCase() == "/pm"){
      if(res.length >= 3){
        if(res[1] == 'SERVER'){
          var help = "You cannot PM the server.";
          socket.emit('annouce', {message : help});
          return;
        }
        if(findUser(res[1])){
          pm(res[1], res.slice(2, res.length).join(' '));
          return;
        } else if (findUser(res.slice(1 ,3).join(' '))){
          pm(res.slice(1 ,3).join(' '), res.slice(3, res.length).join(' '));
          return;
        } else {
          var help = "User not found for command '" + res.join(' ') + "'";
          socket.emit('annouce', {message : help});
        }
      } else {
          var help = "You must add a PM message.";
          socket.emit('annouce', {message : help});
      }
    }

    if(res[0].toLowerCase() == "/disconnect"){
      socket.disconnect();
    }

    if(res[0].toLowerCase() == "/help"){
      sendHelp();
      return;
    }

    unrecognized(res[0]);
    sendHelp();
  }

  var entityMap = {
    "&": "&",
    "<": "<",
    ">": ">",
    '"': '"',
    "'": ''',
    "/": '/'
  };

  function escapeHtml(string) {
    return String(string).replace(/[&<>"'\/]/g, function (s) {
      return entityMap[s];
    });
  }

  function removeItem(arr, item) {
    for(var i = arr.length; i--;) {
      if(arr[i][0].toString() === item[0].toString()) {
        arr.splice(i, 1);
      }
    }
  }

  function findUser(user) {
    for(var i = users.length; i--;) {
      if(users[i][0] == user) {
        return true;
      }
    }
    return false;
  }

  function findSocket(user) {
    for(var i = users.length; i--;) {
      if(users[i][0] == user) {
        return users[i][1];
      }
    }
    return 0;
  }

  function pm(dest, data){
    if(dest == name){
      var help = "You cannot PM yourself.";
      socket.emit('annouce', {message : help});
      return;
    }
    data = '<to ' + dest + '> ' + data + '</span';
    io.sockets.socket(findSocket(dest)).emit('broadcast', {client : name, message : data});
    socket.emit('pm', {client : name, message : data});
  }

  function updateName(data){
    data = escapeHtml(data);
    if(findUser(data)){
      socket.emit('annouce', {message : "The nick '" + data + "' is taken!"});
      return;
    }
    if(data.toLowerCase() == "admin" || data.toLowerCase() == "server"){
      socket.emit('annouce', {message : "That nick is reserved!"});
      return;
    }
    oldName = name;
    name = data;
    removeItem(users, [oldName, socket.id]);
    users.push([name, socket.id, true]);
    io.sockets.emit('users', users);
    socket.emit('name', {name : name});
    broadcast('' + oldName + ' has changed name to ' + data + '.');
  }

  function disconnect(data){
    socket.disconnect();
  }

  function unrecognized(data){
    var help = "Unknown command '" + data + "'.";
    socket.emit('annouce', {message : help});
  }

  function sendHelp(){
    var help = "HardOrange IRC Help - Commands:
/nick nick
/pm nick message
/clear
/disconnect
/help";
    socket.emit('annouce', {message : help});
  }

  function multiArrayIndex(arr, item){
    for(var i = 0; i < arr.length; i++) {
      if(arr[i][0].toString() == item[0].toString()) {
          return i;
       }
     }
     return -1;
  }

  //Begin Handlers
  sendHelp();

  socket.on('config', function (data){
    if(data.name != ''){
      if(findUser(data.name)){
        socket.emit('annouce', {message : "The nick '" + data.name + "' is taken!"});
      } else {
        name = data.name;
      }
    }
    broadcast('' + name + ' has entered chat.');
    users.push([name, socket.id, true]);
    io.sockets.emit('users', users);
    socket.emit('name', {name : name});

    //All Other Handlers

    socket.on('broadcast', function (data) {
      if(data.message[0] == "/") {
        parseServerCommand(data);
      } else {
        bbcode.parse(escapeHtml(data.message), function(content){
          io.sockets.emit('broadcast', {client : name, message : content});
        });
      }
    });

    socket.on('status', function (data) {
      var index = multiArrayIndex(users, [name, socket.id, !data.status]);
      if(index == -1){console.log('missing user: ' + name);return;}
      users[index][2] = data.status;
      io.sockets.emit('users', users);
    });

    socket.on('disconnect', function (data) {
      removeItem(users, [name, socket.id]);
      io.sockets.emit('users', users);
      broadcast('' + name + ' has exited chat.');
    });
  });
});
<commit_msg>Check for empty message<commit_after>var config = require('./config.js');
var ip = config.ip;
var port = config.port;
var express = require('express');
var app = express();
var server = app.listen(port);
var ejs = require('ejs');
var bbcode = require('bbcode');
var io = require('socket.io').listen(server);
io.set('log level', 1);
var sys = require('sys')
var exec = require('child_process').exec;

app.set("view options", {layout: false});
app.use(express.json());
app.use(express.static(__dirname + '/public'));

process.on('SIGINT', function() {
  console.log( "\nShutting down from manual SIGINT (Ctrl-C), NOW." );
  io.sockets.emit('annouce', {message : 'SHUTTING DOWN, NOW!'});
  process.exit();
});

process.once('SIGUSR2', function () {
  gracefulShutdown(function () {
    process.kill(process.pid, 'SIGUSR2');
  });
});

function gracefulShutdown(kill){
  console.log( "\nShutting down from nodemon SIGUSR2 (RESTART) in 10 seconds..." );
  io.sockets.emit('annouce', {message : 'RESTARTING IN 10 SECONDS!'});
  setTimeout(function(){kill()}, 10000);
}

app.post('/gitpull', function(req, res) {
  //GitHub push hook
  res.end();
  io.sockets.emit('annouce', {message : 'SYSTEM UPDATE INITIATED...'});
  function puts(error, stdout, stderr) {sys.puts(stdout)}
  exec("git reset --hard HEAD", puts);
  exec("git pull", puts);
  var arr = req.body.commits[0].modified;
  if((arr.join(',').indexOf("app.min.js") > -1) || (arr.join(',').indexOf("app.min.css") > -1) || (arr.join(',').indexOf("index.ejs") > -1)){
    io.sockets.emit('annouce', {message : 'SYSTEM UPDATE COMPLETE, BROWSER RELOAD IS NECCESARY.'});
  } else {
    io.sockets.emit('annouce', {message : 'SYSTEM UPDATE COMPLETE, BROWSER RELOAD IS NOT NECCESARY.'});
  }
});

app.get('*', function(req, res) {
  res.render('index.ejs', {
   ip: ip
 });
});

var users = [];

process.stdin.resume();
process.stdin.setEncoding('utf8');

process.stdin.on('data', function (chunk) {
 io.sockets.emit('annouce', {message : '' + chunk.toUpperCase() + ''});
});

io.sockets.on('connection', function (socket) {
  var name = 'Guest' + Math.floor(100 + Math.random() * 900);

  function broadcast(data) {
    io.sockets.emit('annouce', {message : data});
  }

  function parseServerCommand(data) {
    var res = data.message.trim().split(" ");

    if(res[0].toLowerCase() == "/nick"){
      updateName(res.slice(1,res.length).join(' '));
      return;
    }

    if(res[0].toLowerCase() == "/pm"){
      if(res.length >= 3){
        if(res[1] == 'SERVER'){
          var help = "You cannot PM the server.";
          socket.emit('annouce', {message : help});
          return;
        }
        if(findUser(res[1])){
          pm(res[1], res.slice(2, res.length).join(' '));
          return;
        } else if (findUser(res.slice(1 ,3).join(' '))){
          pm(res.slice(1 ,3).join(' '), res.slice(3, res.length).join(' '));
          return;
        } else {
          var help = "User not found for command '" + res.join(' ') + "'";
          socket.emit('annouce', {message : help});
        }
      } else {
          var help = "You must add a PM message.";
          socket.emit('annouce', {message : help});
      }
    }

    if(res[0].toLowerCase() == "/disconnect"){
      socket.disconnect();
    }

    if(res[0].toLowerCase() == "/help"){
      sendHelp();
      return;
    }

    unrecognized(res[0]);
    sendHelp();
  }

  var entityMap = {
    "&": "&",
    "<": "<",
    ">": ">",
    '"': '"',
    "'": ''',
    "/": '/'
  };

  function escapeHtml(string) {
    return String(string).replace(/[&<>"'\/]/g, function (s) {
      return entityMap[s];
    });
  }

  function removeItem(arr, item) {
    for(var i = arr.length; i--;) {
      if(arr[i][0].toString() === item[0].toString()) {
        arr.splice(i, 1);
      }
    }
  }

  function findUser(user) {
    for(var i = users.length; i--;) {
      if(users[i][0] == user) {
        return true;
      }
    }
    return false;
  }

  function findSocket(user) {
    for(var i = users.length; i--;) {
      if(users[i][0] == user) {
        return users[i][1];
      }
    }
    return 0;
  }

  function pm(dest, data){
    if(dest == name){
      var help = "You cannot PM yourself.";
      socket.emit('annouce', {message : help});
      return;
    }
    data = '<to ' + dest + '> ' + data + '</span';
    io.sockets.socket(findSocket(dest)).emit('broadcast', {client : name, message : data});
    socket.emit('pm', {client : name, message : data});
  }

  function updateName(data){
    data = escapeHtml(data);
    if(findUser(data)){
      socket.emit('annouce', {message : "The nick '" + data + "' is taken!"});
      return;
    }
    if(data.toLowerCase() == "admin" || data.toLowerCase() == "server"){
      socket.emit('annouce', {message : "That nick is reserved!"});
      return;
    }
    oldName = name;
    name = data;
    removeItem(users, [oldName, socket.id]);
    users.push([name, socket.id, true]);
    io.sockets.emit('users', users);
    socket.emit('name', {name : name});
    broadcast('' + oldName + ' has changed name to ' + data + '.');
  }

  function disconnect(data){
    socket.disconnect();
  }

  function unrecognized(data){
    var help = "Unknown command '" + data + "'.";
    socket.emit('annouce', {message : help});
  }

  function sendHelp(){
    var help = "HardOrange IRC Help - Commands:
/nick nick
/pm nick message
/clear
/disconnect
/help";
    socket.emit('annouce', {message : help});
  }

  function multiArrayIndex(arr, item){
    for(var i = 0; i < arr.length; i++) {
      if(arr[i][0].toString() == item[0].toString()) {
          return i;
       }
     }
     return -1;
  }

  //Begin Handlers
  sendHelp();

  socket.on('config', function (data){
    if(data.name != ''){
      if(findUser(data.name)){
        socket.emit('annouce', {message : "The nick '" + data.name + "' is taken!"});
      } else {
        name = data.name;
      }
    }
    broadcast('' + name + ' has entered chat.');
    users.push([name, socket.id, true]);
    io.sockets.emit('users', users);
    socket.emit('name', {name : name});

    //All Other Handlers

    socket.on('broadcast', function (data) {
      if(data === undefined){
        console.log('Undefined Message Received');
        return;
      }
      if(data.message[0] == "/") {
        parseServerCommand(data);
      } else {
        bbcode.parse(escapeHtml(data.message), function(content){
          io.sockets.emit('broadcast', {client : name, message : content});
        });
      }
    });

    socket.on('status', function (data) {
      var index = multiArrayIndex(users, [name, socket.id, !data.status]);
      if(index == -1){console.log('missing user: ' + name);return;}
      users[index][2] = data.status;
      io.sockets.emit('users', users);
    });

    socket.on('disconnect', function (data) {
      removeItem(users, [name, socket.id]);
      io.sockets.emit('users', users);
      broadcast('' + name + ' has exited chat.');
    });
  });
});
<|endoftext|>


<commit_before>var express = require('express');
var app = express();

app.use(express.static(__dirname + '/public'));

var port = process.env.PORT || 8989;

var server = app.listen(port);

var io = require('socket.io')(server);

var farms = {};

var Farm = function(pw, row, col) {
	this.gold = 100;
	this.farm = [];
	for (var i = 0; i < row; i++) {
		this.farm.push([]);
		for (var j = 0; j < col; j++) {
			this.farm[i].push({row: i, col: j, name: ""});
		}
	}
	this.inventory = [];
	this.inventory.push({plant: new Plant(), quantity: 3});
	this.password = pw;
};

var Plant = function() {
	this.name = "carrot";
	this.age = 0;
	this.ripetime = 10;
	this.yield = 0;
	this.color = "#FF0000";
	this.shape = 0;
	this.hash = this.name+this.ripetime+this.yield+this.color+this.shape;
};

console.log("Farmland is running on port 8989");
/*
   var farm = new Farm();
   farms["William"] = farm;
   console.log(new Farm());
   plant("William", new Plant(), 3, 3);
   pick("William", 3, 3);
   grow()
   console.log(farms);*/

function plantFood(owner, food, row, col) {
	var inventory = farms[owner].inventory;
	var plant = farms[owner].farm[row][col];
	var found = false;
	inventory.forEach(function(item) {
		if(food.hash == item.plant.hash && item.quantity > 0) {
			item.quantity--;
			plant = item.plant;
			found = true;
		}
	});
	return found;
}

function pickFood(owner, row, col) {
	var plant = farms[owner].farm[row][col];
	var inventory = farms[owner].inventory;
	if (plant.hash != undefined && plant.age > plant.ripetime) {
		var found = false;
		inventory.forEach(function(item) {
			if(item.hash == plant.hash) {
				item.quantity++;
				plant = {row: row, col: col};
				found = true;
			}
		});
		if(!found) {
			inventory.push({plant: plant, quantity: 3});
			plant = {row: row, col: col};
		}
		return true;
	}
	return false;
}

function growFood() {
	var keys = Object.keys(farms);
	keys.forEach(function(key) {
		if (farms[key]) {
			farms[key].farm.forEach(function(row) {
				row.forEach(function(plant) {
					if (plant.age !== undefined) {
						plant.age++;
					}
				});
			});
		}
	});
}

setInterval(growFood, 1000);

io.on('connection', function(socket) {
  var name = "";
  socket.on("createFarm", function(nm, pw, cb) {
    name = nm;
    if (farms[nm] === undefined) {
      var farm = new Farm(pw, 7, 7);
      farms[nm] = farm;
      cb(farm);
    } else if(farms[nm].password == pw) {
			cb(farms[nm]);
		} else {
      cb(false);
    }
  });
  socket.on("plantFood", function(nm, crop, row, col, cb) {
    cb(plantFood(nm, crop, row, col));
  });
  socket.on("pickFood", function(nm, row, col, cb) {
    cb(pickFood(nm, row, col));
  });
});
<commit_msg>planted the food for realz<commit_after>var express = require('express');
var app = express();

app.use(express.static(__dirname + '/public'));

var port = process.env.PORT || 8989;

var server = app.listen(port);

var io = require('socket.io')(server);

var farms = {};

var Farm = function(pw, row, col) {
	this.gold = 100;
	this.farm = [];
	for (var i = 0; i < row; i++) {
		this.farm.push([]);
		for (var j = 0; j < col; j++) {
			this.farm[i].push({row: i, col: j, name: ""});
		}
	}
	this.inventory = [];
	this.inventory.push({plant: new Plant(), quantity: 3});
	this.password = pw;
};

var Plant = function() {
	this.name = "carrot";
	this.age = 0;
	this.ripetime = 10;
	this.yield = 0;
	this.color = "#FF0000";
	this.shape = 0;
	this.hash = this.name+this.ripetime+this.yield+this.color+this.shape;
};

console.log("Farmland is running on port 8989");
/*
   var farm = new Farm();
   farms["William"] = farm;
   console.log(new Farm());
   plant("William", new Plant(), 3, 3);
   pick("William", 3, 3);
   grow()
   console.log(farms);*/

function plantFood(owner, food, row, col) {
	var inventory = farms[owner].inventory;
	var plant = farms[owner].farm[row][col];
	console.log(plant.hash);
	if(plant.hash != undefined) {
		return false;
	}
	var found = false;
	inventory.forEach(function(item) {
		if(food.hash == item.plant.hash && item.quantity > 0) {
			item.quantity--;
			farms[owner].farm[row][col] = item.plant;
			found = true;
		}
	});
	return found;
}

function pickFood(owner, row, col) {
	var plant = farms[owner].farm[row][col];
	var inventory = farms[owner].inventory;
	if (plant.hash != undefined && plant.age > plant.ripetime) {
		var found = false;
		inventory.forEach(function(item) {
			if(item.hash == plant.hash) {
				item.quantity++;
				plant = {row: row, col: col};
				found = true;
			}
		});
		if(!found) {
			inventory.push({plant: plant, quantity: 3});
			plant = {row: row, col: col};
		}
		return true;
	}
	return false;
}

function growFood() {
	var keys = Object.keys(farms);
	keys.forEach(function(key) {
		if (farms[key]) {
			farms[key].farm.forEach(function(row) {
				row.forEach(function(plant) {
					if (plant.age !== undefined) {
						plant.age++;
					}
				});
			});
		}
	});
}

setInterval(growFood, 1000);

io.on('connection', function(socket) {
  var name = "";
  socket.on("createFarm", function(nm, pw, cb) {
    name = nm;
    if (farms[nm] === undefined) {
      var farm = new Farm(pw, 7, 7);
      farms[nm] = farm;
      cb(farm);
    } else if(farms[nm].password == pw) {
			cb(farms[nm]);
		} else {
      cb(false);
    }
  });
  socket.on("plantFood", function(nm, crop, row, col, cb) {
    cb(plantFood(nm, crop, row, col));
  });
  socket.on("pickFood", function(nm, row, col, cb) {
    cb(pickFood(nm, row, col));
  });
});
<|endoftext|>


<commit_before>/* Osmium
 * Copyright (C) 2013 Romain "Artefact2" Dalmaso <artefact2@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

argsh = function(x) {
	return Math.log(x + Math.sqrt(x*x + 1.0));
};

osmium_targeting_time = function(targeter_scanres, targetee_sigradius) {
	/* Formula taken from the official wiki */
	return 40000.0 / (targeter_scanres * Math.pow(argsh(targetee_sigradius), 2));
};

osmium_gen_fattribs = function() {
	$("section#defense > h4 > span.pname").text(osmium_clf['X-damage-profile'][0]);

	var t = "Scan resolution\n\nTime to lock…";
	var sr = parseFloat($("span#scan_resolution").data('value'));
	var sig = parseFloat($("p#signature_radius").data('value'));

	for(var i = 0; i < osmium_targetclass.length; ++i) {
		if(!osmium_targetclass[i][1]) continue;
		t += "\n" + osmium_targetclass[i][0] + ": "
			+ osmium_targeting_time(sr, osmium_targetclass[i][3]).toFixed(1) + " s"
	}

	$("span#scan_resolution").prop('title', t);

	t = "Signature radius\n\nTime to be locked by…";

	for(var i = 0; i < osmium_targetclass.length; ++i) {
		if(!osmium_targetclass[i][2]) continue;
		t += "\n" + osmium_targetclass[i][0] + ": "
			+ osmium_targeting_time(osmium_targetclass[i][4], sig).toFixed(1) + " s"
	}

	$("p#signature_radius").prop('title', t);
};

osmium_init_fattribs = function() {
	osmium_ctxmenu_bind($("div#computed_attributes > section#defense"), function() {
		var menu = osmium_ctxmenu_create();

		osmium_ctxmenu_add_subctxmenu(menu, "Damage profiles", function() {
			var smenu = osmium_ctxmenu_create();

			for(var k in osmium_damage_profiles) {
				osmium_ctxmenu_add_subctxmenu(smenu, k, (function(profiles) {
					return function() {
						var ssmenu = osmium_ctxmenu_create();

						for(var z in profiles) {
							var opts = {
								toggled: z === osmium_clf['X-damage-profile'][0]
							};

							if(profiles[z].length >= 5) {
								opts.icon = profiles[z][4];
							}

							osmium_ctxmenu_add_option(ssmenu, z, (function(name, profile) {
								return function() {
									osmium_clf['X-damage-profile'] = [ name, profile.slice(0, 4) ];
									osmium_undo_push();
									osmium_commit_clf();
								};
							})(z, profiles[z]), opts);
						}
						return ssmenu;
					};
				})(osmium_damage_profiles[k]), {});
			}

			osmium_ctxmenu_add_subctxmenu(smenu, "Custom", function() {
				var ssmenu = osmium_ctxmenu_create();
				var count = 0;

				for(var k in osmium_custom_damage_profiles) {
					++count;

					osmium_ctxmenu_add_subctxmenu(ssmenu, k, (function(k, profile) {
						return function() {
							var sssmenu = osmium_ctxmenu_create();

							osmium_ctxmenu_add_option(sssmenu, "Use", function() {
								osmium_clf['X-damage-profile'] = [ k, profile ];
								osmium_undo_push();
								osmium_commit_clf();
							}, { default: true });

							osmium_ctxmenu_add_option(sssmenu, "Remove", function() {
								if(k === osmium_clf['X-damage-profile'][0]) {
									osmium_clf['X-damage-profile'] = [ "Uniform", [ .25, .25, .25, .25 ] ];
									osmium_commit_clf();
								}

								delete osmium_custom_damage_profiles[k];
								osmium_commit_custom_damage_profiles();
							}, {});

							return sssmenu;
						};
					})(k, osmium_custom_damage_profiles[k]), {
						toggled: k === osmium_clf['X-damage-profile'][0]
					});
				}

				if(count >= 1) {
					osmium_ctxmenu_add_separator(ssmenu);
				}

				osmium_ctxmenu_add_option(ssmenu, "Create…", function() {
					var name = prompt("Damage profile name:");
					if(!name) return;
					var prof = prompt("Damage values (EM, Explosive, Kinetic, Thermal):",
									  "[ 0.25, 0.25, 0.25, 0.25 ]");
					if(!prof) return;

					try {
						var p = JSON.parse(prof);
						var s;

						if(p[0] < 0 || p[1] < 0 || p[2] < 0 || p[3] < 0 || (s = p[0] + p[1] + p[2] + p[3]) <= 0) {
							alert("Incorrect damage values: all four numbers must be positive, and at least one of them must be nonzero.");
						} else {
							osmium_custom_damage_profiles[name] = [
								p[0] / s, p[1] / s, p[2] / s, p[3] / s
							];

							osmium_clf['X-damage-profile'] = [ name, osmium_custom_damage_profiles[name] ];
							osmium_undo_push();
							osmium_commit_clf();
							osmium_commit_custom_damage_profiles();
						}
					} catch(e) {
						alert("Incorrect syntax in damage values.");
					}

					$("ul#ctxmenu").trigger('delete_menu');
				}, {});

				return ssmenu;
			}, {});

			return smenu;
		}, {});

		return menu;
	});
};

osmium_commit_custom_damage_profiles = function() {
	$.ajax({
		type: 'POST',
		url: osmium_relative + '/src/ajax/put_custom_damage_profiles.php?' + $.param({ token: osmium_token }),
		data: { payload: JSON.stringify(osmium_custom_damage_profiles) }
	});
};
<commit_msg>Show damage repartition/sig radius/scan res in relevant tooltips.<commit_after>/* Osmium
 * Copyright (C) 2013 Romain "Artefact2" Dalmaso <artefact2@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

argsh = function(x) {
	return Math.log(x + Math.sqrt(x*x + 1.0));
};

osmium_targeting_time = function(targeter_scanres, targetee_sigradius) {
	/* Formula taken from the official wiki */
	return 40000.0 / (targeter_scanres * Math.pow(argsh(targetee_sigradius), 2));
};

osmium_gen_fattribs = function() {
	$("section#defense > h4 > span.pname").text(osmium_clf['X-damage-profile'][0]);

	var t = "Scan resolution\n\nTime to lock…";
	var sr = parseFloat($("span#scan_resolution").data('value'));
	var sig = parseFloat($("p#signature_radius").data('value'));

	for(var i = 0; i < osmium_targetclass.length; ++i) {
		if(!osmium_targetclass[i][1]) continue;
		t += "\n" + osmium_targetclass[i][0] + " (" + osmium_targetclass[i][3].toFixed(0) + " m): "
			+ osmium_targeting_time(sr, osmium_targetclass[i][3]).toFixed(1) + " s"
	}

	$("span#scan_resolution").prop('title', t);

	t = "Signature radius\n\nTime to be locked by…";

	for(var i = 0; i < osmium_targetclass.length; ++i) {
		if(!osmium_targetclass[i][2]) continue;
		t += "\n" + osmium_targetclass[i][0] + " (" + osmium_targetclass[i][4].toFixed(0) + " mm) : "
			+ osmium_targeting_time(osmium_targetclass[i][4], sig).toFixed(1) + " s"
	}

	$("p#signature_radius").prop('title', t);
};

osmium_init_fattribs = function() {
	osmium_ctxmenu_bind($("div#computed_attributes > section#defense"), function() {
		var menu = osmium_ctxmenu_create();

		osmium_ctxmenu_add_subctxmenu(menu, "Damage profiles", function() {
			var smenu = osmium_ctxmenu_create();

			for(var k in osmium_damage_profiles) {
				osmium_ctxmenu_add_subctxmenu(smenu, k, (function(profiles) {
					return function() {
						var ssmenu = osmium_ctxmenu_create();

						for(var z in profiles) {
							var s = profiles[z][0] + profiles[z][1] + profiles[z][2] + profiles[z][3];

							var opts = {
								toggled: z === osmium_clf['X-damage-profile'][0],
								title: (100 * profiles[z][0] / s).toFixed(1) + "% EM, "
									+ (100 * profiles[z][1] / s).toFixed(1) + "% Explosive, "
									+ (100 * profiles[z][2] / s).toFixed(1) + "% Kinetic, "
									+ (100 * profiles[z][3] / s).toFixed(1) + "% Thermal"
							};

							if(profiles[z].length >= 5) {
								opts.icon = profiles[z][4];
							}

							osmium_ctxmenu_add_option(ssmenu, z, (function(name, profile) {
								return function() {
									osmium_clf['X-damage-profile'] = [ name, profile.slice(0, 4) ];
									osmium_undo_push();
									osmium_commit_clf();
								};
							})(z, profiles[z]), opts);
						}
						return ssmenu;
					};
				})(osmium_damage_profiles[k]), {});
			}

			osmium_ctxmenu_add_subctxmenu(smenu, "Custom", function() {
				var ssmenu = osmium_ctxmenu_create();
				var count = 0;

				for(var k in osmium_custom_damage_profiles) {
					++count;

					osmium_ctxmenu_add_subctxmenu(ssmenu, k, (function(k, profile) {
						return function() {
							var sssmenu = osmium_ctxmenu_create();

							osmium_ctxmenu_add_option(sssmenu, "Use", function() {
								osmium_clf['X-damage-profile'] = [ k, profile ];
								osmium_undo_push();
								osmium_commit_clf();
							}, { default: true });

							osmium_ctxmenu_add_option(sssmenu, "Remove", function() {
								if(k === osmium_clf['X-damage-profile'][0]) {
									osmium_clf['X-damage-profile'] = [ "Uniform", [ .25, .25, .25, .25 ] ];
									osmium_commit_clf();
								}

								delete osmium_custom_damage_profiles[k];
								osmium_commit_custom_damage_profiles();
							}, {});

							return sssmenu;
						};
					})(k, osmium_custom_damage_profiles[k]), {
						toggled: k === osmium_clf['X-damage-profile'][0]
					});
				}

				if(count >= 1) {
					osmium_ctxmenu_add_separator(ssmenu);
				}

				osmium_ctxmenu_add_option(ssmenu, "Create…", function() {
					var name = prompt("Damage profile name:");
					if(!name) return;
					var prof = prompt("Damage values (EM, Explosive, Kinetic, Thermal):",
									  "[ 0.25, 0.25, 0.25, 0.25 ]");
					if(!prof) return;

					try {
						var p = JSON.parse(prof);
						var s;

						if(p[0] < 0 || p[1] < 0 || p[2] < 0 || p[3] < 0 || (s = p[0] + p[1] + p[2] + p[3]) <= 0) {
							alert("Incorrect damage values: all four numbers must be positive, and at least one of them must be nonzero.");
						} else {
							osmium_custom_damage_profiles[name] = [
								p[0] / s, p[1] / s, p[2] / s, p[3] / s
							];

							osmium_clf['X-damage-profile'] = [ name, osmium_custom_damage_profiles[name] ];
							osmium_undo_push();
							osmium_commit_clf();
							osmium_commit_custom_damage_profiles();
						}
					} catch(e) {
						alert("Incorrect syntax in damage values.");
					}

					$("ul#ctxmenu").trigger('delete_menu');
				}, {});

				return ssmenu;
			}, {});

			return smenu;
		}, {});

		return menu;
	});
};

osmium_commit_custom_damage_profiles = function() {
	$.ajax({
		type: 'POST',
		url: osmium_relative + '/src/ajax/put_custom_damage_profiles.php?' + $.param({ token: osmium_token }),
		data: { payload: JSON.stringify(osmium_custom_damage_profiles) }
	});
};
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013, Joyent, Inc. All rights reserved.
 *
 * lib/agent/agent.js: in-zone agent responsible for maintaining configuration
 *     files
 *
 * This agent makes extensive use of objects called configuration manifests.
 * These manifests describe all data associated with a configuration file,
 * including but not limited to its template, where that file is installed, and
 * a command to run after the file is installed.  These manifests are generated
 * by and retrieved from the Services API (SAPI).
 *
 * The full list of fields includes:
 *
 * 	name		The manifest's name.
 *
 * 	template	The file's hogan.js template.
 *
 *	contents	The rendered configuration file, basically the
 *			template + metadata.  This is not actually stored in
 *			SAPI but rather generated here in the zone.
 *
 *	path		Where this file is installed.
 *
 *	post_cmd	(Optional) If a file is updated, this command is run
 *			after the file is updated.
 *
 * In addition to the configuration manfiests, each zone also has a set of
 * metadata values.  These values are used to render the configuration file from
 * the manifest's template.  These values are stored separately from each
 * manfiest since the metadata values apply to all manifests in a zone.
 */

var assert = require('assert-plus');
var async = require('async');
var exec = require('child_process').exec;
var fs = require('fs');
var hogan = require('hogan.js');
var path = require('path');
var sdc = require('sdc-clients');
var sprintf = require('util').format;
var vasync = require('vasync');

var mkdirp = require('mkdirp');

function Agent(config) {
	assert.object(config, 'config');
	assert.string(config.zonename, 'config.zonename');
	assert.object(config.log, 'config.log');
	assert.object(config.sapi, 'config.sapi');
	assert.string(config.sapi.url, 'config.sapi.url');

	this.zonename = config.zonename;

	this.log = config.log;

	this.sapi = new sdc.SAPI({
		log: config.log,
		url: config.sapi.url,
		agent: false
	});
}

Agent.prototype.checkAndRefresh = function checkAndRefresh(cb) {
	var self = this;
	var log = self.log;
	var sapi = self.sapi;

	async.waterfall([
		function querySapi(subcb) {
			sapi.getConfig(self.zonename, function (err, config) {
				if (err) {
					log.error(err, 'failed to get config');
					return (subcb(err));
				}

				assert.object(config, 'config');

				log.debug({ config: config },
				    'read config from SAPI');

				return (subcb(null,
				    config.manifests, config.metadata));
			});
		},
		function renderFiles(manifests, metadata, subcb) {
			assert.arrayOfObject(manifests, 'manifests');
			assert.object(metadata, 'metadata');

			log.debug({
			    manifests: manifests,
			    metadata: metadata
			}, 'read manifests and metadata');

			manifests.forEach(function (manifest) {
				renderConfigFile.call(self, manifest, metadata);
			});
			return (subcb(null, manifests));
		},
		function writeFiles(manifests, subcb) {
			vasync.forEachParallel({
				func: writeConfigFile.bind(self),
				inputs: manifests
			}, function (err) {
				return (subcb(err));
			});
		}
	], function (err, results) {
		if (err)
			log.error(err, 'failed to write all files');

		if (cb)
			cb();
	});
};

function runPostCommand(post_cmd, cb) {
	var self = this;
	var log = self.log;

	assert.string(post_cmd, 'post_cmd');

	exec(post_cmd, function (err) {
		if (err)
			log.error(err, 'post_cmd "%s" failed', post_cmd);

		return (cb(err));
	});
}

/*
 * Render a configuration file from its manifest.
 */
function renderConfigFile(manifest, metadata) {
	var self = this;
	var log = self.log;

	assert.object(manifest, 'manifest');
	assert.string(manifest.template, 'manifest.template');

	assert.object(metadata, 'metadata');

	log.info('rendering configuration file from manifest %s',
	    manifest.name);

	var contents = hogan.compile(manifest.template).render(metadata);

	if (!contents) {
		log.error('failed to render configuration file');
	} else {
		log.debug({ contents: contents },
		    'rendered configuration file');
	}

	manifest.contents = contents;

	log.debug({ contents: contents }, 'generated contents for manifest %s',
	    manifest.name);

	return (manifest);
}

function writeConfigFile(manifest, cb) {
	var self = this;
	var log = self.log;

	assert.object(manifest, 'manifest');
	assert.string(manifest.name, 'manifest.name');
	assert.string(manifest.path, 'manifest.path');
	assert.string(manifest.contents, 'manifest.contents');

	var contents = manifest.contents;
	var existing = null;

	async.waterfall([
		function (subcb) {
			var dirname = path.dirname(manifest.path);

			log.debug('mkdir -p %s', dirname);

			mkdirp(dirname, function (err) {
				if (err) {
					log.warn(err,
					    'failed to mkdir -p %s', dirname);
				}

				subcb(null);
			});
		},
		function (subcb) {
			fs.readFile(manifest.path, 'ascii',
			    function (err, file) {
				if (err) {
					log.warn(err, 'failed to read file %s',
					    manifest.path);
					return (subcb(null));
				}

				existing = file;
				return (subcb(null));
			});
		},
		function (subcb) {
			if (existing && contents === existing) {
				log.info('file %s unchaged; not updating ' +
				    'file', manifest.path);
				return (subcb(null, false));
			}

			log.info('writing file into %s', manifest.path);

			fs.writeFile(manifest.path, contents, function (err) {
				if (err) {
					log.error(err, 'failed to write ' +
					    'file %s', manifest.path);
					return (subcb(err));
				}

				log.info('updated file for manifest %s',
				    manifest.name);

				return (subcb(null, true));
			});

			return (null);
		},
		function (updated, subcb) {
			assert.bool(updated, 'updated');
			assert.func(subcb, 'subcb');

			if (!updated)
				return (subcb(null));

			if (!manifest.post_cmd) {
				log.debug('no post command to run for ' +
				    'manifest %s', manifest.name);
				return (subcb(null));
			}

			runPostCommand.call(self, manifest.post_cmd,
			    function (err) {
				/*
				 * If the post command fails, it's not a fatal
				 * error.  The failure will be logged in the
				 * runPostCommand() function.
				 */
				subcb(null);
			});
			return (null);
		}
	], function (err) {
		if (err) {
			log.error(err, 'failed to update file for manifest %s',
			    manifest.name);
			return (cb(err));
		}

		return (cb(null));
	});
}

module.exports = Agent;
<commit_msg>SAPI-77 config-agent enters maintenance when passed an invalid template<commit_after>/*
 * Copyright (c) 2013, Joyent, Inc. All rights reserved.
 *
 * lib/agent/agent.js: in-zone agent responsible for maintaining configuration
 *     files
 *
 * This agent makes extensive use of objects called configuration manifests.
 * These manifests describe all data associated with a configuration file,
 * including but not limited to its template, where that file is installed, and
 * a command to run after the file is installed.  These manifests are generated
 * by and retrieved from the Services API (SAPI).
 *
 * The full list of fields includes:
 *
 * 	name		The manifest's name.
 *
 * 	template	The file's hogan.js template.
 *
 *	contents	The rendered configuration file, basically the
 *			template + metadata.  This is not actually stored in
 *			SAPI but rather generated here in the zone.
 *
 *	path		Where this file is installed.
 *
 *	post_cmd	(Optional) If a file is updated, this command is run
 *			after the file is updated.
 *
 * In addition to the configuration manfiests, each zone also has a set of
 * metadata values.  These values are used to render the configuration file from
 * the manifest's template.  These values are stored separately from each
 * manfiest since the metadata values apply to all manifests in a zone.
 */

var assert = require('assert-plus');
var async = require('async');
var exec = require('child_process').exec;
var fs = require('fs');
var hogan = require('hogan.js');
var path = require('path');
var sdc = require('sdc-clients');
var sprintf = require('util').format;
var vasync = require('vasync');

var mkdirp = require('mkdirp');

function Agent(config) {
	assert.object(config, 'config');
	assert.string(config.zonename, 'config.zonename');
	assert.object(config.log, 'config.log');
	assert.object(config.sapi, 'config.sapi');
	assert.string(config.sapi.url, 'config.sapi.url');

	this.zonename = config.zonename;

	this.log = config.log;

	this.sapi = new sdc.SAPI({
		log: config.log,
		url: config.sapi.url,
		agent: false
	});
}

Agent.prototype.checkAndRefresh = function checkAndRefresh(cb) {
	var self = this;
	var log = self.log;
	var sapi = self.sapi;

	async.waterfall([
		function querySapi(subcb) {
			sapi.getConfig(self.zonename, function (err, config) {
				if (err) {
					log.error(err, 'failed to get config');
					return (subcb(err));
				}

				assert.object(config, 'config');

				log.debug({ config: config },
				    'read config from SAPI');

				return (subcb(null,
				    config.manifests, config.metadata));
			});
		},
		function renderFiles(manifests, metadata, subcb) {
			assert.arrayOfObject(manifests, 'manifests');
			assert.object(metadata, 'metadata');

			log.debug({
			    manifests: manifests,
			    metadata: metadata
			}, 'read manifests and metadata');

			manifests.forEach(function (manifest) {
				renderConfigFile.call(self, manifest, metadata);
			});
			return (subcb(null, manifests));
		},
		function writeFiles(manifests, subcb) {
			vasync.forEachParallel({
				func: writeConfigFile.bind(self),
				inputs: manifests
			}, function (err) {
				return (subcb(err));
			});
		}
	], function (err, results) {
		if (err)
			log.error(err, 'failed to write all files');

		if (cb)
			cb();
	});
};

function runPostCommand(post_cmd, cb) {
	var self = this;
	var log = self.log;

	assert.string(post_cmd, 'post_cmd');

	exec(post_cmd, function (err) {
		if (err)
			log.error(err, 'post_cmd "%s" failed', post_cmd);

		return (cb(err));
	});
}

/*
 * Render a configuration file from its manifest.
 */
function renderConfigFile(manifest, metadata) {
	var self = this;
	var log = self.log;

	assert.object(manifest, 'manifest');
	assert.string(manifest.template, 'manifest.template');

	assert.object(metadata, 'metadata');

	log.info('rendering configuration file from manifest %s',
	    manifest.name);

	var contents = null;
	try {
		contents = hogan.compile(manifest.template).render(metadata);
	} catch (e) {
		log.error('invalid hogan template: ' + e.message);
	}

	if (!contents) {
		log.error('failed to render configuration file');
	} else {
		log.debug({ contents: contents },
		    'rendered configuration file');
	}

	manifest.contents = contents;

	log.debug({ contents: contents }, 'generated contents for manifest %s',
	    manifest.name);

	return (manifest);
}

function writeConfigFile(manifest, cb) {
	var self = this;
	var log = self.log;

	assert.object(manifest, 'manifest');
	assert.string(manifest.name, 'manifest.name');
	assert.string(manifest.path, 'manifest.path');
	assert.string(manifest.contents, 'manifest.contents');

	var contents = manifest.contents;
	var existing = null;

	async.waterfall([
		function (subcb) {
			var dirname = path.dirname(manifest.path);

			log.debug('mkdir -p %s', dirname);

			mkdirp(dirname, function (err) {
				if (err) {
					log.warn(err,
					    'failed to mkdir -p %s', dirname);
				}

				subcb(null);
			});
		},
		function (subcb) {
			fs.readFile(manifest.path, 'ascii',
			    function (err, file) {
				if (err) {
					log.warn(err, 'failed to read file %s',
					    manifest.path);
					return (subcb(null));
				}

				existing = file;
				return (subcb(null));
			});
		},
		function (subcb) {
			if (existing && contents === existing) {
				log.info('file %s unchaged; not updating ' +
				    'file', manifest.path);
				return (subcb(null, false));
			}

			log.info('writing file into %s', manifest.path);

			fs.writeFile(manifest.path, contents, function (err) {
				if (err) {
					log.error(err, 'failed to write ' +
					    'file %s', manifest.path);
					return (subcb(err));
				}

				log.info('updated file for manifest %s',
				    manifest.name);

				return (subcb(null, true));
			});

			return (null);
		},
		function (updated, subcb) {
			assert.bool(updated, 'updated');
			assert.func(subcb, 'subcb');

			if (!updated)
				return (subcb(null));

			if (!manifest.post_cmd) {
				log.debug('no post command to run for ' +
				    'manifest %s', manifest.name);
				return (subcb(null));
			}

			runPostCommand.call(self, manifest.post_cmd,
			    function (err) {
				/*
				 * If the post command fails, it's not a fatal
				 * error.  The failure will be logged in the
				 * runPostCommand() function.
				 */
				subcb(null);
			});
			return (null);
		}
	], function (err) {
		if (err) {
			log.error(err, 'failed to update file for manifest %s',
			    manifest.name);
			return (cb(err));
		}

		return (cb(null));
	});
}

module.exports = Agent;
<|endoftext|>


<commit_before>// Dependencies
var fs = require('fs');
var url = require('url');
var http = require('http');
var jsdom = require("jsdom");
var exec = require('child_process').exec;

var thread = process.argv[2];
var host = url.parse(thread);
var folderName = host.pathname.split('/').pop();
var mkdir = 'mkdir -p ' + 'downloads/' + folderName;
var chmod = 'chmod -R 777 downloads/' + folderName;

var mkdirExec = exec(mkdir, function (err, stdout, stderr) {
    if (err) {
        throw err;
    } else {
        console.log('folder created');
    }
});
var chmodExec = exec(chmod, function (err, stdout, stderr) {
    if (err) {
        throw err;
    }
});

var links = [];

jsdom.env(
    thread,
    ["http://code.jquery.com/jquery.js"],
    function (err, window) {
        images = window.$(".postContainer .file .fileThumb");
        for (i = 0; i < images.length; i++) {
            links.push(window.$(images[i]).attr('href'));
        }
       for (i = 0; i < links.length;i++) {
           href = 'http://'+links[i].replace(/\/\//, '');
           splitted = href.split("/");
           filename = splitted[splitted.length - 1];

           var request = http.get(href, function(res){
               var imagedata = '';
               res.setEncoding('binary');

               res.on('data', function(chunk){
                   imagedata += chunk
               });

               res.on('end', function(){
                   fs.writeFile(filename, imagedata, 'binary', function(err){
                       if (err) {
                           throw err
                       }
                       console.log(filename + ' saved.')
                   });
               })
           })
       }
    }
);

<commit_msg>use http-request because...<commit_after>// Dependencies
var url = require('url');
var http = require('http-request');
var jsdom = require("jsdom");
var exec = require('child_process').exec;

var thread = process.argv[2];
var host = url.parse(thread);
var folderName = host.pathname.split('/').pop();
var mkdir = 'mkdir -p ' + 'downloads/' + folderName;
var chmod = 'chmod -R 777 downloads/' + folderName;

var mkdirExec = exec(mkdir, function (err, stdout, stderr) {
    if (err) {
        throw err;
    } else {
        console.log('folder created');
    }
});
var chmodExec = exec(chmod, function (err, stdout, stderr) {
    if (err) {
        throw err;
    }
});

var links = [];

jsdom.env(
    thread,
    ["http://code.jquery.com/jquery.js"],
    function (err, window) {
        images = window.$(".postContainer .file .fileThumb");
        for (i = 0; i < images.length; i++) {
            links.push(window.$(images[i]).attr('href'));
        }
       for (i = 0; i < links.length;i++) {
           href = 'http://'+links[i].replace(/\/\//, '');
           splitted = href.split("/");
           filename = splitted[splitted.length - 1];
           fullPath = 'downloads/' + folderName + '/' + filename;

           options = {url: href};

           http.get(options, fullPath, function (error, result) {
               if (error) {
                   console.error(error);
               } else {
                   console.log('File downloaded at: ' + result.file);
               }
           });
       }
    }
);

<|endoftext|>


<commit_before>import {
  forEach, last, uuid, EventEmitter, platform, isString, documentHelpers, prettyPrintXML
} from 'substance'
import { throwMethodIsAbstract } from '../kit/shared'
import ManifestLoader from './ManifestLoader'
import ManifestEditorSession from './ManifestEditorSession'

/*
  A PersistedDocumentArchive is a 3-tier stack representing a document archive
  at different application levels:

  1. Editor: an application such as Texture works on an in-memory data model,
     managed by EditorSessions. There may be multiple sessions for different parts of the
     document archive, e.g. the manuscript and an entity db.
  2. Buffer: a short-term storage for pending changes. Until the document archive
     is saved permanently, changes are recorded and can be persisted, e.g. to
     avoid loosing changes when the browser is closed inadvertently.
  3. Storage: a long-term storage where the document archive is persisted and versioned.

  PersistedDocumentArchive manages the communication between the three layers, e.g.
  when the user changes a document, it records the change and stores it into the buffer,
  and eventually saving a new version of the ardhive.
*/
export default class PersistedDocumentArchive extends EventEmitter {
  constructor (storage, buffer, context, config) {
    super()
    this.storage = storage
    this.buffer = buffer

    this._archiveId = null
    this._upstreamArchive = null
    this._sessions = null
    this._pendingFiles = new Map()
    this._config = config
  }

  addDocument (type, name, xml) {
    let documentId = uuid()
    let sessions = this._sessions
    let session = this._loadDocument(type, { data: xml }, sessions)
    sessions[documentId] = session
    this._registerForSessionChanges(session, documentId)
    this._addDocumentRecord(documentId, type, name, documentId + '.xml')
    return documentId
  }

  addAsset (file) {
    let assetId = uuid()
    let [name, ext] = _getNameAndExtension(file.name)
    let filePath = this._getUniqueFileName(name, ext)
    this._getManifestEditorSession().transaction(tx => {
      let assetNode = tx.create({
        type: 'asset',
        id: assetId,
        path: filePath,
        assetType: file.type
      })
      documentHelpers.append(tx, ['dar', 'assets'], assetNode.id)
    })
    this.buffer.addBlob(assetId, {
      id: assetId,
      path: filePath,
      blob: file
    })
    // ATTENTION: blob urls are not supported in nodejs
    // and I do not see that this is really necessary
    // For sake of testing we use `PSEUDO-BLOB-URL:${filePath}`
    // so that we can see if the rest of the system is working
    if (platform.inBrowser) {
      this._pendingFiles.set(filePath, URL.createObjectURL(file))
    } else {
      this._pendingFiles.set(filePath, `PSEUDO-BLOB-URL:${filePath}`)
    }
    return filePath
  }

  getAsset (fileName) {
    return this._sessions.manifest.getDocument().getAssetByPath(fileName)
  }

  getDocumentEntries () {
    return this.getDocumentSession('manifest').getDocument().getDocumentEntries()
  }

  getDownloadLink (fileName) {
    let manifest = this._sessions.manifest.getDocument()
    let asset = manifest.getAssetByPath(fileName)
    if (asset) {
      return this.resolveUrl(fileName)
    }
  }

  getDocumentSession (docId) {
    return this._sessions[docId]
  }

  hasAsset (fileName) {
    // TODO: at some point I want to introduce an index for files by fileName/path
    return Boolean(this.getAsset(fileName))
  }

  hasPendingChanges () {
    return this.buffer.hasPendingChanges()
  }

  load (archiveId, cb) {
    const storage = this.storage
    const buffer = this.buffer
    storage.read(archiveId, (err, upstreamArchive) => {
      if (err) return cb(err)
      buffer.load(archiveId, err => {
        if (err) return cb(err)
        // Ensure that the upstream version is compatible with the buffer.
        // The buffer may contain pending changes.
        // In this case the buffer should be based on the same version
        // as the latest version in the storage.
        if (!buffer.hasPendingChanges()) {
          let localVersion = buffer.getVersion()
          let upstreamVersion = upstreamArchive.version
          if (localVersion && upstreamVersion && localVersion !== upstreamVersion) {
            // If the local version is out-of-date, it would be necessary to 'rebase' the
            // local changes.
            console.error('Upstream document has changed. Discarding local changes')
            this.buffer.reset(upstreamVersion)
          } else {
            buffer.reset(upstreamVersion)
          }
        }
        // convert raw archive into sessions (=ingestion)
        let sessions = this._ingest(upstreamArchive)
        // contract: there must be a manifest
        if (!sessions['manifest']) {
          throw new Error('There must be a manifest session.')
        }
        // apply pending changes
        if (!buffer.hasPendingChanges()) {
          // TODO: when we have a persisted buffer we need to apply all pending
          // changes.
          // For now, we always start with a fresh buffer
        } else {
          buffer.reset(upstreamArchive.version)
        }
        // register for any changes in each session
        this._registerForAllChanges(sessions)

        this._archiveId = archiveId
        this._upstreamArchive = upstreamArchive
        this._sessions = sessions

        cb(null, this)
      })
    })
  }

  removeDocument (documentId) {
    let session = this._sessions[documentId]
    this._unregisterFromSession(session)
    this._getManifestEditorSession().transaction(tx => {
      documentHelpers.remove(tx, ['dar', 'documents'], documentId)
      documentHelpers.deleteNode(documentId)
    })
  }

  renameDocument (documentId, name) {
    this._getManifestEditorSession().transaction(tx => {
      let documentNode = tx.get(documentId)
      documentNode.name = name
    })
  }

  resolveUrl (path) {
    // until saved, files have a blob URL
    let blobUrl = this._pendingFiles.get(path)
    if (blobUrl) {
      return blobUrl
    } else {
      let fileRecord = this._upstreamArchive.resources[path]
      if (fileRecord && fileRecord.encoding === 'url') {
        return fileRecord.data
      }
    }
  }

  save (cb) {
    // FIXME: buffer.hasPendingChanges() is not working
    this.buffer._isDirty['manuscript'] = true
    this._save(this._archiveId, cb)
  }

  /*
    Save as is implemented as follows.

    1. clone: copy all files from original archive to new archive (backend)
    2. save: perform a regular save using user buffer (over new archive, including pending
       documents and blobs)
  */
  saveAs (newArchiveId, cb) {
    this.storage.clone(this._archiveId, newArchiveId, (err) => {
      if (err) return cb(err)
      this._save(newArchiveId, cb)
    })
  }

  /*
    Adds a document record to the manifest file
  */
  _addDocumentRecord (documentId, type, name, path) {
    this._getManifestEditorSession().transaction(tx => {
      let documentNode = tx.create({
        type: 'document',
        id: documentId,
        documentType: type,
        name,
        path
      })
      documentHelpers.append(tx, ['dar', 'documents', documentNode.id])
    })
  }

  _getManifestEditorSession () {
    if (!this._manifestEditorSession) {
      this._manifestEditorSession = new ManifestEditorSession('manifest', this._sessions.manifest)
    }
    return this._manifestEditorSession
  }

  _getUniqueFileName (name, ext) {
    let candidate
    // first try the canonical one
    candidate = `${name}.${ext}`
    if (this.hasAsset(candidate)) {
      let count = 2
      // now use a suffix counting up
      while (true) {
        candidate = `${name}_${count++}.${ext}`
        if (!this.hasAsset(candidate)) break
      }
    }

    return candidate
  }

  _loadManifest (record) {
    if (!record) {
      throw new Error('manifest.xml is missing')
    }
    return ManifestLoader.load(record.data)
  }

  _registerForAllChanges (sessions) {
    forEach(sessions, (session, docId) => {
      this._registerForSessionChanges(session, docId)
    })
  }

  _registerForSessionChanges (session, docId) {
    session.on('change', (change) => {
      this.buffer.addChange(docId, change)
      // Apps can subscribe to this (e.g. to show there's pending changes)
      this.emit('archive:changed')
    }, this)
  }

  _repair () {
    // no-op
  }

  /*
    Create a raw archive for upload from the changed resources.
  */
  _save (archiveId, cb) {
    const buffer = this.buffer
    const storage = this.storage
    const sessions = this._sessions

    let rawArchiveUpdate = this._exportChanges(sessions, buffer)

    // CHALLENGE: we either need to lock the buffer, so that
    // new changes are interfering with ongoing sync
    // or we need something pretty smart caching changes until the
    // sync has succeeded or failed, e.g. we could use a second buffer in the meantime
    // probably a fast first-level buffer (in-mem) is necessary anyways, even in conjunction with
    // a slower persisted buffer
    storage.write(archiveId, rawArchiveUpdate, (err, res) => {
      // TODO: this need to implemented in a more robust fashion
      // i.e. we should only reset the buffer if storage.write was successful
      if (err) return cb(err)

      // TODO: if successful we should receive the new version as response
      // and then we can reset the buffer
      let _res = { version: '0' }
      if (isString(res)) {
        try {
          _res = JSON.parse(res)
        } catch (err) {
          console.error('Invalid response from storage.write()')
        }
      }
      // console.log('Saved. New version:', res.version)
      buffer.reset(_res.version)
      // revoking object urls
      if (platform.inBrowser) {
        for (let blobUrl of this._pendingFiles.values()) {
          window.URL.revokeObjectURL(blobUrl)
        }
      }
      this._pendingFiles.clear()

      // After successful save the archiveId may have changed (save as use case)
      this._archiveId = archiveId
      this.emit('archive:saved')
      cb(null, rawArchiveUpdate)
    })
  }

  _unregisterFromSession (session) {
    session.off(this)
  }

  /*
    Uses the current state of the buffer to generate a rawArchive object
    containing all changed documents
  */
  _exportChanges (sessions, buffer) {
    let rawArchive = {
      version: buffer.getVersion(),
      diff: buffer.getChanges(),
      resources: {}
    }
    this._exportManifest(sessions, buffer, rawArchive)
    this._exportChangedDocuments(sessions, buffer, rawArchive)
    this._exportChangedAssets(sessions, buffer, rawArchive)
    return rawArchive
  }

  _exportManifest (sessions, buffer, rawArchive) {
    let manifest = sessions.manifest.getDocument()
    if (buffer.hasResourceChanged('manifest')) {
      let manifestDom = manifest.toXML()
      let manifestXmlStr = prettyPrintXML(manifestDom)
      rawArchive.resources['manifest.xml'] = {
        id: 'manifest',
        data: manifestXmlStr,
        encoding: 'utf8',
        updatedAt: Date.now()
      }
    }
  }

  // TODO: generalize the implementation so that it can live here
  _exportChangedDocuments (sessions, buffer, rawArchive) {
    throwMethodIsAbstract()
  }

  _exportChangedAssets (sessions, buffer, rawArchive) {
    let manifest = sessions.manifest.getDocument()
    let assetNodes = manifest.getAssetNodes()
    assetNodes.forEach(asset => {
      let assetId = asset.id
      if (buffer.hasBlobChanged(assetId)) {
        let path = asset.attr('path') || assetId
        let blobRecord = buffer.getBlob(assetId)
        rawArchive.resources[path] = {
          assetId,
          data: blobRecord.blob,
          encoding: 'blob',
          createdAt: Date.now(),
          updatedAt: Date.now()
        }
      }
    })
  }
}

function _getNameAndExtension (name) {
  let frags = name.split('.')
  let ext = ''
  if (frags.length > 1) {
    ext = last(frags)
    name = frags.slice(0, frags.length - 1).join('.')
  }
  return [name, ext]
}
<commit_msg>Fix regression in PersistedDocumentArchive.<commit_after>import {
  forEach, last, uuid, EventEmitter, platform, isString, documentHelpers, prettyPrintXML
} from 'substance'
import { throwMethodIsAbstract } from '../kit/shared'
import ManifestLoader from './ManifestLoader'
import ManifestEditorSession from './ManifestEditorSession'

/*
  A PersistedDocumentArchive is a 3-tier stack representing a document archive
  at different application levels:

  1. Editor: an application such as Texture works on an in-memory data model,
     managed by EditorSessions. There may be multiple sessions for different parts of the
     document archive, e.g. the manuscript and an entity db.
  2. Buffer: a short-term storage for pending changes. Until the document archive
     is saved permanently, changes are recorded and can be persisted, e.g. to
     avoid loosing changes when the browser is closed inadvertently.
  3. Storage: a long-term storage where the document archive is persisted and versioned.

  PersistedDocumentArchive manages the communication between the three layers, e.g.
  when the user changes a document, it records the change and stores it into the buffer,
  and eventually saving a new version of the ardhive.
*/
export default class PersistedDocumentArchive extends EventEmitter {
  constructor (storage, buffer, context, config) {
    super()
    this.storage = storage
    this.buffer = buffer

    this._archiveId = null
    this._upstreamArchive = null
    this._sessions = null
    this._pendingFiles = new Map()
    this._config = config
  }

  addDocument (type, name, xml) {
    let documentId = uuid()
    let sessions = this._sessions
    let session = this._loadDocument(type, { data: xml }, sessions)
    sessions[documentId] = session
    this._registerForSessionChanges(session, documentId)
    this._addDocumentRecord(documentId, type, name, documentId + '.xml')
    return documentId
  }

  addAsset (file) {
    let assetId = uuid()
    let [name, ext] = _getNameAndExtension(file.name)
    let filePath = this._getUniqueFileName(name, ext)
    this._getManifestEditorSession().transaction(tx => {
      let assetNode = tx.create({
        type: 'asset',
        id: assetId,
        path: filePath,
        assetType: file.type
      })
      documentHelpers.append(tx, ['dar', 'assets'], assetNode.id)
    })
    this.buffer.addBlob(assetId, {
      id: assetId,
      path: filePath,
      blob: file
    })
    // ATTENTION: blob urls are not supported in nodejs
    // and I do not see that this is really necessary
    // For sake of testing we use `PSEUDO-BLOB-URL:${filePath}`
    // so that we can see if the rest of the system is working
    if (platform.inBrowser) {
      this._pendingFiles.set(filePath, URL.createObjectURL(file))
    } else {
      this._pendingFiles.set(filePath, `PSEUDO-BLOB-URL:${filePath}`)
    }
    return filePath
  }

  getAsset (fileName) {
    return this._sessions.manifest.getDocument().getAssetByPath(fileName)
  }

  getDocumentEntries () {
    return this.getDocumentSession('manifest').getDocument().getDocumentEntries()
  }

  getDownloadLink (fileName) {
    let manifest = this._sessions.manifest.getDocument()
    let asset = manifest.getAssetByPath(fileName)
    if (asset) {
      return this.resolveUrl(fileName)
    }
  }

  getDocumentSession (docId) {
    return this._sessions[docId]
  }

  hasAsset (fileName) {
    // TODO: at some point I want to introduce an index for files by fileName/path
    return Boolean(this.getAsset(fileName))
  }

  hasPendingChanges () {
    return this.buffer.hasPendingChanges()
  }

  load (archiveId, cb) {
    const storage = this.storage
    const buffer = this.buffer
    storage.read(archiveId, (err, upstreamArchive) => {
      if (err) return cb(err)
      buffer.load(archiveId, err => {
        if (err) return cb(err)
        // Ensure that the upstream version is compatible with the buffer.
        // The buffer may contain pending changes.
        // In this case the buffer should be based on the same version
        // as the latest version in the storage.
        if (!buffer.hasPendingChanges()) {
          let localVersion = buffer.getVersion()
          let upstreamVersion = upstreamArchive.version
          if (localVersion && upstreamVersion && localVersion !== upstreamVersion) {
            // If the local version is out-of-date, it would be necessary to 'rebase' the
            // local changes.
            console.error('Upstream document has changed. Discarding local changes')
            this.buffer.reset(upstreamVersion)
          } else {
            buffer.reset(upstreamVersion)
          }
        }
        // convert raw archive into sessions (=ingestion)
        let sessions = this._ingest(upstreamArchive)
        // contract: there must be a manifest
        if (!sessions['manifest']) {
          throw new Error('There must be a manifest session.')
        }
        // apply pending changes
        if (!buffer.hasPendingChanges()) {
          // TODO: when we have a persisted buffer we need to apply all pending
          // changes.
          // For now, we always start with a fresh buffer
        } else {
          buffer.reset(upstreamArchive.version)
        }
        // register for any changes in each session
        this._registerForAllChanges(sessions)

        this._archiveId = archiveId
        this._upstreamArchive = upstreamArchive
        this._sessions = sessions

        cb(null, this)
      })
    })
  }

  removeDocument (documentId) {
    let session = this._sessions[documentId]
    this._unregisterFromSession(session)
    this._getManifestEditorSession().transaction(tx => {
      documentHelpers.remove(tx, ['dar', 'documents'], documentId)
      documentHelpers.deleteNode(documentId)
    })
  }

  renameDocument (documentId, name) {
    this._getManifestEditorSession().transaction(tx => {
      let documentNode = tx.get(documentId)
      documentNode.name = name
    })
  }

  resolveUrl (path) {
    // until saved, files have a blob URL
    let blobUrl = this._pendingFiles.get(path)
    if (blobUrl) {
      return blobUrl
    } else {
      let fileRecord = this._upstreamArchive.resources[path]
      if (fileRecord && fileRecord.encoding === 'url') {
        return fileRecord.data
      }
    }
  }

  save (cb) {
    // FIXME: buffer.hasPendingChanges() is not working
    this.buffer._isDirty['manuscript'] = true
    this._save(this._archiveId, cb)
  }

  /*
    Save as is implemented as follows.

    1. clone: copy all files from original archive to new archive (backend)
    2. save: perform a regular save using user buffer (over new archive, including pending
       documents and blobs)
  */
  saveAs (newArchiveId, cb) {
    this.storage.clone(this._archiveId, newArchiveId, (err) => {
      if (err) return cb(err)
      this._save(newArchiveId, cb)
    })
  }

  /*
    Adds a document record to the manifest file
  */
  _addDocumentRecord (documentId, type, name, path) {
    this._getManifestEditorSession().transaction(tx => {
      let documentNode = tx.create({
        type: 'document',
        id: documentId,
        documentType: type,
        name,
        path
      })
      documentHelpers.append(tx, ['dar', 'documents', documentNode.id])
    })
  }

  _getManifestEditorSession () {
    if (!this._manifestEditorSession) {
      this._manifestEditorSession = new ManifestEditorSession('manifest', this._sessions.manifest)
    }
    return this._manifestEditorSession
  }

  _getUniqueFileName (name, ext) {
    let candidate
    // first try the canonical one
    candidate = `${name}.${ext}`
    if (this.hasAsset(candidate)) {
      let count = 2
      // now use a suffix counting up
      while (true) {
        candidate = `${name}_${count++}.${ext}`
        if (!this.hasAsset(candidate)) break
      }
    }

    return candidate
  }

  _loadManifest (record) {
    if (!record) {
      throw new Error('manifest.xml is missing')
    }
    return ManifestLoader.load(record.data)
  }

  _registerForAllChanges (sessions) {
    forEach(sessions, (session, docId) => {
      this._registerForSessionChanges(session, docId)
    })
  }

  _registerForSessionChanges (session, docId) {
    session.on('change', (change) => {
      this.buffer.addChange(docId, change)
      // Apps can subscribe to this (e.g. to show there's pending changes)
      this.emit('archive:changed')
    }, this)
  }

  _repair () {
    // no-op
  }

  /*
    Create a raw archive for upload from the changed resources.
  */
  _save (archiveId, cb) {
    const buffer = this.buffer
    const storage = this.storage
    const sessions = this._sessions

    let rawArchiveUpdate = this._exportChanges(sessions, buffer)

    // CHALLENGE: we either need to lock the buffer, so that
    // new changes are interfering with ongoing sync
    // or we need something pretty smart caching changes until the
    // sync has succeeded or failed, e.g. we could use a second buffer in the meantime
    // probably a fast first-level buffer (in-mem) is necessary anyways, even in conjunction with
    // a slower persisted buffer
    storage.write(archiveId, rawArchiveUpdate, (err, res) => {
      // TODO: this need to implemented in a more robust fashion
      // i.e. we should only reset the buffer if storage.write was successful
      if (err) return cb(err)

      // TODO: if successful we should receive the new version as response
      // and then we can reset the buffer
      let _res = { version: '0' }
      if (isString(res)) {
        try {
          _res = JSON.parse(res)
        } catch (err) {
          console.error('Invalid response from storage.write()')
        }
      }
      // console.log('Saved. New version:', res.version)
      buffer.reset(_res.version)
      // revoking object urls
      if (platform.inBrowser) {
        for (let blobUrl of this._pendingFiles.values()) {
          window.URL.revokeObjectURL(blobUrl)
        }
      }
      this._pendingFiles.clear()

      // After successful save the archiveId may have changed (save as use case)
      this._archiveId = archiveId
      this.emit('archive:saved')
      cb(null, rawArchiveUpdate)
    })
  }

  _unregisterFromSession (session) {
    session.off(this)
  }

  /*
    Uses the current state of the buffer to generate a rawArchive object
    containing all changed documents
  */
  _exportChanges (sessions, buffer) {
    let rawArchive = {
      version: buffer.getVersion(),
      diff: buffer.getChanges(),
      resources: {}
    }
    this._exportManifest(sessions, buffer, rawArchive)
    this._exportChangedDocuments(sessions, buffer, rawArchive)
    this._exportChangedAssets(sessions, buffer, rawArchive)
    return rawArchive
  }

  _exportManifest (sessions, buffer, rawArchive) {
    let manifest = sessions.manifest.getDocument()
    if (buffer.hasResourceChanged('manifest')) {
      let manifestDom = manifest.toXML()
      let manifestXmlStr = prettyPrintXML(manifestDom)
      rawArchive.resources['manifest.xml'] = {
        id: 'manifest',
        data: manifestXmlStr,
        encoding: 'utf8',
        updatedAt: Date.now()
      }
    }
  }

  // TODO: generalize the implementation so that it can live here
  _exportChangedDocuments (sessions, buffer, rawArchive) {
    throwMethodIsAbstract()
  }

  _exportChangedAssets (sessions, buffer, rawArchive) {
    let manifest = sessions.manifest.getDocument()
    let assetNodes = manifest.getAssetNodes()
    assetNodes.forEach(asset => {
      let assetId = asset.id
      if (buffer.hasBlobChanged(assetId)) {
        let path = asset.path || assetId
        let blobRecord = buffer.getBlob(assetId)
        rawArchive.resources[path] = {
          assetId,
          data: blobRecord.blob,
          encoding: 'blob',
          createdAt: Date.now(),
          updatedAt: Date.now()
        }
      }
    })
  }
}

function _getNameAndExtension (name) {
  let frags = name.split('.')
  let ext = ''
  if (frags.length > 1) {
    ext = last(frags)
    name = frags.slice(0, frags.length - 1).join('.')
  }
  return [name, ext]
}
<|endoftext|>


<commit_before>var http = require('http');
var path = require('path');
var fs = require('fs');
var gitHubWebhookHandler = require('github-webhook-handler');
var settings = {};

settings.hooks = [{name: 'gitHubPuller', localPath: ''}];

for (var i = 0; i < settings.hooks.length; i++) {
	var handler = gitHubWebhookHandler({ path: settings.hooks[i].name, secret: 'lorcanvida' });
	handler.on('push', function (event) {
		var added = event.payload.head_commit.added,
	        removed = event.payload.head_commit.removed,
	        modified = event.payload.head_commit.modified,
	        remotePath = event.payload.url,
	        j;

	    for (j = 0; j < modified.length; j++) {
		    http.get(remotePath + '/' + added[j], function(err, res) {
			    if (err) { errorHandler('push1', err); return; }
			    fs.write(path.join(settings.localPath, added[j]), res.body, function(err) {
                    if (err) { errorHandler('push2', err); return; }
                });
		    });
	    }

        console.log('Received a push event for %s to %s',
            event.payload.repository.name,
            event.payload.ref);
        /// get https://github.com/seethespark/i-flicks/archive/master.zip
	    });
    handler.on('error', function (err) {
        console.error('Error:', err.message);
    });

    settings.hooks[i].handler = handler;
}
http.createServer(function (req, res) {
	for (var i = 0; i < settings.hooks.length; i++) {
		settings.hooks[i].handler(req, res, function (err) {
	        res.statusCode = 404;
	        res.end('no such location');
	    });
	}
}).listen(7777);

function errorHandler(err, location, res) {
    console.log('Error at ', location, '.', 'Message: ', err.message);
    if (res) {
        res.status(500);
        res.end();
    }
}

/*
gitHubPullerHandler.on('issues', function (event) {
    console.log('Received an issue event for %s action=%s: #%d %s',
        event.payload.repository.name,
        event.payload.action,
        event.payload.issue.number,
        event.payload.issue.title);
});*/<commit_msg>just for test<commit_after>var http = require('http');
var path = require('path');
var fs = require('fs');
var gitHubWebhookHandler = require('github-webhook-handler');
var settings = {};

settings.hooks = [{name: 'gitHubPuller', localPath: ''}];

for (var i = 0; i < settings.hooks.length; i++) {
	var handler = gitHubWebhookHandler({ path: settings.hooks[i].name, secret: 'lorcanvida' });
	handler.on('push', function (event) {
		var added = event.payload.head_commit.added,
	        removed = event.payload.head_commit.removed,
	        modified = event.payload.head_commit.modified,
	        remotePath = event.payload.url,
	        j;

	    for (j = 0; j < modified.length; j++) {
		    http.get(remotePath + '/' + added[j], function(err, res) {
			    if (err) { errorHandler('push1', err); return; }
			    fs.write(path.join(settings.localPath, added[j]), res.body, function(err) {
                    if (err) { errorHandler('push2', err); return; }
                });
		    });
	    }

        console.log('Received a push event for %s to %s',
            event.payload.repository.name,
            event.payload.ref);
        /// get https://github.com/seethespark/i-flicks/archive/master.zip
	    });
    handler.on('error', function (err) {
        console.error('Error:', err.message);
    });

    settings.hooks[i].handler = handler;
}
http.createServer(function (req, res) {
	for (var i = 0; i < settings.hooks.length; i++) {
		settings.hooks[i].handler(req, res, function (err) {
	        res.statusCode = 404;
	        res.end('no such location');
	    });
	}
}).listen(7777);

function errorHandler(err, location, res) {
    console.log('Error at ', location, '.', 'Message: ', err.message);
    if (res) {
        res.status(500);
        res.end();
    }
}

/*
gitHubPullerHandler.on('issues', function (event) {
    console.log('Received an issue event for %s action=%s: #%d %s',
        event.payload.repository.name,
        event.payload.action,
        event.payload.issue.number,
        event.payload.issue.title);
});*/<|endoftext|>


<commit_before>/**
 * Вернуть полное тело заданного шаблона
 * при наследовании
 *
 * @param {string} tplName - название шаблона
 * @return {string}
 */
DirObj.prototype.getFullBody = function (tplName) {
	const
		protoLength = 'proto'.length,
		constLength = 1;

	const
		isDecl = [];

	forIn(this.getGroup('inherit'), (el, key) => {
		isDecl.push(key);
	});

	const
		length = isDecl.length * 2,
		is = {};

	for (let i = -1, j = 0; ++i < isDecl.length;) {
		is[i + j] = isDecl[i];
		j++;
		is[i + j] = `${isDecl[i]}_add`;
	}

	const
		parentTpl = extMap[tplName];

	let
		res = cache[parentTpl];

	const
		alb = ADV_LEFT_BLOCK,
		lb = LEFT_BLOCK,
		rb = RIGHT_BLOCK;

	if (!this.tolerateWhitespace) {
		res += `${alb}${lb}__&-__${rb}`;
	}

	let
		from = 0,
		advDiff = [];

	const sornFn =
		(a, b) => a.val - b.val;

	let
		tb = table[tplName],
		k;

	let
		el,
		prev;

	let
		newFrom,
		blockDiff;

	for (let i = -1; ++i < length;) {
		const type = is[i];

		if (routerCache[type]) {
			k = `${type}_`;

			el = routerCache[type][tplName];
			prev = routerCache[type][parentTpl];

			if (routerFromCache[type]) {
				from = routerFromCache[type][parentTpl];
				newFrom = null;
			}
		}

		for (let key in el) {
			if (!el.hasOwnProperty(key)) {
				continue;
			}

			// Сдвиг относительно
			// родительской позиции элемента
			let adv = 0;

			const
				current = el[key],
				parent = !tb[k + key].drop && prev[key];

			let block = cache[tplName]
				.substring(current.from, current.to);

			// Разница между дочерним и родительским блоком
			if (parent) {
				if (parent.output != null && current.output == null && (i % 2 === 0)) {
					current.output = parent.output;

					if (type === 'const') {
						block += current.output;

					} else {
						this.getBlockOutput(type, tplName)[key] = current.output;
					}
				}

				blockDiff = block.length -
					cache[parentTpl].substring(parent.from, parent.to).length;
			}

			const
				diff = parent ?
					parent.from : from;

			advDiff.sort(sornFn);

			for (let j = -1; ++j < advDiff.length;) {
				if (advDiff[j].val <= diff) {
					adv += advDiff[j].adv;

				} else {
					break;
				}
			}

			// Переопределение
			if (parent && (i % 2 === 0)) {
				if (type !== 'block' && (type !== 'const' || !current.block)) {
					newFrom = parent.from + adv + block.length;
					from += blockDiff;

					if (newFrom > from) {
						from = newFrom + (type === 'proto' ? protoLength : constLength);
					}
				}

				res = res.substring(0, parent.from + adv) +
					block +
					res.substring(parent.to + adv);

				advDiff.push({
					val: parent.from,
					adv: blockDiff
				});

			// Добавление
			} else if (!parent) {
				if (type === 'block_add') {
					res += block;

				} else if (type === 'const_add' || type === 'proto_add') {

					// Случай, если в дочернем шаблоне нет перекрытий,
					// но есть добавления нового
					if (newFrom === null) {
						newFrom = from;
						from += adv;
					}

					block = type === 'const_add' ?
						`${current.needPrfx ? alb : ''}${lb}${block}${rb}` : block;

					res = res.substring(0, from) +
						block +
						res.substring(from);

					advDiff.push({
						val: newFrom,
						adv: block.length
					});

					from = from + block.length;
				}
			}
		}
	}

	return res;
};
<commit_msg>Translated in English<commit_after>/**
 * Returns the full body of a template
 * (with inheritance)
 *
 * @param {string} tplName - the template name
 * @return {string}
 */
DirObj.prototype.getFullBody = function (tplName) {
	const
		protoLength = 'proto'.length,
		constLength = 1;

	const
		isDecl = [];

	forIn(this.getGroup('inherit'), (el, key) => {
		isDecl.push(key);
	});

	const
		length = isDecl.length * 2,
		is = {};

	for (let i = -1, j = 0; ++i < isDecl.length;) {
		is[i + j] = isDecl[i];
		j++;
		is[i + j] = `${isDecl[i]}_add`;
	}

	const
		parentTpl = extMap[tplName];

	let
		res = cache[parentTpl];

	const
		alb = ADV_LEFT_BLOCK,
		lb = LEFT_BLOCK,
		rb = RIGHT_BLOCK;

	if (!this.tolerateWhitespace) {
		res += `${alb}${lb}__&-__${rb}`;
	}

	let
		from = 0,
		advDiff = [];

	const sornFn =
		(a, b) => a.val - b.val;

	let
		tb = table[tplName],
		k;

	let
		el,
		prev;

	let
		newFrom,
		blockDiff;

	for (let i = -1; ++i < length;) {
		const type = is[i];

		if (routerCache[type]) {
			k = `${type}_`;

			el = routerCache[type][tplName];
			prev = routerCache[type][parentTpl];

			if (routerFromCache[type]) {
				from = routerFromCache[type][parentTpl];
				newFrom = null;
			}
		}

		for (let key in el) {
			if (!el.hasOwnProperty(key)) {
				continue;
			}

			// The shift relative
			// position of the parent element
			let adv = 0;

			const
				current = el[key],
				parent = !tb[k + key].drop && prev[key];

			let block = cache[tplName]
				.substring(current.from, current.to);

			// The difference between the child and the parent elements
			if (parent) {
				if (parent.output != null && current.output == null && (i % 2 === 0)) {
					current.output = parent.output;

					if (type === 'const') {
						block += current.output;

					} else {
						this.getBlockOutput(type, tplName)[key] = current.output;
					}
				}

				blockDiff = block.length -
					cache[parentTpl].substring(parent.from, parent.to).length;
			}

			const
				diff = parent ?
					parent.from : from;

			advDiff.sort(sornFn);

			for (let j = -1; ++j < advDiff.length;) {
				if (advDiff[j].val <= diff) {
					adv += advDiff[j].adv;

				} else {
					break;
				}
			}

			// Replace
			if (parent && (i % 2 === 0)) {
				if (type !== 'block' && (type !== 'const' || !current.block)) {
					newFrom = parent.from + adv + block.length;
					from += blockDiff;

					if (newFrom > from) {
						from = newFrom + (type === 'proto' ? protoLength : constLength);
					}
				}

				res = res.substring(0, parent.from + adv) +
					block +
					res.substring(parent.to + adv);

				advDiff.push({
					val: parent.from,
					adv: blockDiff
				});

			// Add
			} else if (!parent) {
				if (type === 'block_add') {
					res += block;

				} else if (type === 'const_add' || type === 'proto_add') {

					// If the child template hasn't overloads,
					// But adding a new
					if (newFrom === null) {
						newFrom = from;
						from += adv;
					}

					block = type === 'const_add' ?
						`${current.needPrfx ? alb : ''}${lb}${block}${rb}` : block;

					res = res.substring(0, from) +
						block +
						res.substring(from);

					advDiff.push({
						val: newFrom,
						adv: block.length
					});

					from = from + block.length;
				}
			}
		}
	}

	return res;
};
<|endoftext|>