<commit_before>"use strict"
var repl = require('repl');
var io = require('socket.io');

var randInt = (min, max) => {
  return Math.floor(Math.random() * (max - min)) + min;
};
class Player {
  constructor(server, id) {
    var config = this.config = server.config;
    this.id = id;
    this.clan = null;
    this.server = server;
    this.untilSend = config.clientSendRate;
    this.name = 'unknown'
    this.aimAngle = 0
    this.movement = null
    this.kill();
  }
  updateMovement(delta) {
    var config = this.config;
    var tx = 0;
    var ty = 0;
    if (this.movement != null) {
      tx = Math.cos(this.movement);
      ty = Math.sin(this.movement);
      var i = Math.sqrt(tx * tx + ty * ty);
      if (i !== 0) {
        tx /= i;
        ty /= i;
      }
    }
    this.vx *= Math.pow(config.playerDecel, delta);
    this.vy *= Math.pow(config.playerDecel, delta);
    this.vx += tx * config.playerSpeed * delta;
    this.vy += ty * config.playerSpeed * delta;
    this.x += this.vx;
    this.y += this.vy;
  }
  update(delta) {
    if (this.alive) {
      this.updateMovement(delta);
      this.untilSend--
      if (!this.untilSend) {
        this.untilSend = this.config.clientSendRate;
        this.sendPosition();
      }
    }
  }
  sendPosition() {
    var socket = this.socket;
    socket.emit('a');
    socket.emit('3', [
      this.id,
      this.x,
      this.y, -2.26,-1,0,0,null,0,0,0,0]);
    socket.emit("2",[socket.id,this.id,this.name,this.x,this.y,0,100,100,35,0],true)
  }
  link(socket) {
    this.socket = socket;
    socket.once('error', err => {
      console.log(err);
      this.destroy();
    });
    socket.on('2', angle => this.aimAngle = angle);
    socket.on('3', angle => this.movement = angle);
    socket.once('disconnect', () => this.destroy());
    socket.emit('id', {
      teams: this.server.clans
    });
    socket.on('1', data => {
      this.name = data.name.length > 15 || !data.name ? 'unknown' : data.name;
      this.spawn();
      socket.emit('1', this.id);
    });
  }
  destroy() {
    this.kill();
    this.server.remove(this.id);
    this.socket.disconnect();
  }
  kill() {
    this.alive = false;
    this.x = 0;
    this.y = 0;
    this.slowDown();
  }
  slowDown() {
    this.vx = 0;
    this.vy = 0;
  }
  spawn() {
    this.alive = true;
    this.x = randInt(0, this.config.mapScale);
    this.y = randInt(0, this.config.mapScale);
    this.slowDown();
  }
}
class Server {
  constructor(config) {
    this.config = config;
    this.players = Array(config.maxPlayers).fill(null);
    this.lastRun = Date.now();
    this.clans = [];
    setInterval(() => this.update(), config.serverUpdateRate);
  }
  remove(sid) {
    this.players[sid] = null;
  }
  update() {
    var now = Date.now();
    var delta = this.lastRun - now;
    this.lastRun = now;
    for (var i of this.players) {
      if (i != null) {
        i.update(delta);
      }
    }
  }
  handle(socket) {
    for (var i = 0; i < 50; i++) {
      if (this.players[i] == null) {
        var player = new Player(this, i);
        player.link(socket);
        this.players[i] = player;
        break;
      }
    }
  }
}

var app = new Server({
  mapScale: 14400,
  maxPlayers: 50,
  clientSendRate: 5,
  serverUpdateRate: 9,
  playerDecel: 0.993,
  playerSpeed: 0.0016
});

for (var i = 5000; i <= 5010; i++) {
  io(i).on('connection', socket => app.handle(socket));
}
/*
var teams = [];
var sockets = [];
  io(i).on('connection', function (socket) {
    var emit = (...arg) => {
      try {
        socket.broadcast.emit(...arg);
        socket.emit(...arg);
      } catch (e) {
        sockets.forEach(a => a.emit(...arg));
      }
    };
    sockets.push(socket);
    socket.on('1', function (data) {
      var id = 27;
      var name = data.name.length > 15 ? 'unknown' : data.name;
      socket.emit('id', {
        "teams": teams
      });
      socket.emit('1', id);
      socket.emit('mm', 0);
      socket.emit('3', []);
      var x = randInt(0, config.mapScale);
      var y = randInt(0, config.mapScale);
      var userteam = null;
      var last = 0;
      socket.emit("2", ["a3Pm5dMzeKOjc5gvAJEF", id, name, x, 7200, 0, 100, 100, 35, data.skin], true);
      socket.emit("5", [27, data.name, 9001]); //[5,"RIP",31988,45,"KADEJO503",23404,34,"winter wolf",4821,28,"Godenot",4500,33,"Arena Closer",3000,32,"LightTheif",2940,6,"CarlosKoS-_-16",2800,4,"GD desconhecido",2635,35,"jack black GD",2357,19,"AMIGO BOM",1623])
      socket.emit("6", [
        29, 3115.1, 13592.9, 0, 109.2, 1, null, -1,
        353, 4103, 13436, 0, 80, 2, null, -1,
        339, 2498, 14155, 0, 90, 2, null, -1
      ]);
      socket.emit("8", 2.6, 28);
      socket.on("ch", data => {
        emit("ch", id, data);
      });
      socket.on("8", data => {
        console.log('CLAN CREATE REQUEST:', data);
        teams.push([{
          "sid": data,
          "owner": id
        }]);
        var userteam = data;
        emit("st", data, true);
      });
      socket.on("3", function (data) {
        if (data == null) {
          vx = vy = 0;
          return;
        } else if (x < 0 || x > config.mapScale || y < 0 || y > config.mapScale) {
          x = 7200;
          y = 7200;
        }
        if (y >= 6840 && y <= 7560) {
          x += 0.44;
        }
        var speed = y > 2400 ? 60 * 0.8 : 60;
        vx = Math.cos(data) * speed;
        vy = Math.sin(data) * speed;
        x += vx;
        y += vy;
        socket.emit('a');
        socket.emit("3", [id, x, y, -2.26, -1, 0, 0, null, 0, 0, 0, 0]);
      });
      socket.on("14", function (data) {
        console.log('ping!!!', arguments);
      });
    });
  });
}
*/
repl.start({
  eval: (a, _c, _f, cb) => {
    try {
      cb(null, eval(a)); // jshint ignore: line
    } catch (e) {
      cb(e);
    }
  }
});
<commit_msg>bug fix<commit_after>"use strict"
var repl = require('repl');
var io = require('socket.io');

var randInt = (min, max) => {
  return Math.floor(Math.random() * (max - min)) + min;
};
class Player {
  constructor(server, id) {
    var config = this.config = server.config;
    this.id = id;
    this.clan = null;
    this.server = server;
    this.untilSend = config.clientSendRate;
    this.name = 'unknown'
    this.aimAngle = 0
    this.movement = null
    this.kill();
  }
  updateMovement(delta) {
    var config = this.config;
    var tx = 0;
    var ty = 0;
    if (this.movement != null) {
      tx = Math.cos(this.movement);
      ty = Math.sin(this.movement);
      /*var i = Math.sqrt(tx * tx + ty * ty);
      if (i !== 0) {
        tx /= i;
        ty /= i;
      }*/
    }
    this.vx *= Math.pow(config.playerDecel, delta);
    this.vy *= Math.pow(config.playerDecel, delta);
    this.vx += tx * config.playerSpeed * delta * 35;
    this.vy += ty * config.playerSpeed * delta * 35;
    this.x += this.vx;
    this.y += this.vy;
  }
  update(delta) {
    if (this.alive) {
      this.updateMovement(delta);
      this.untilSend--
      if (!this.untilSend) {
        this.untilSend = this.config.clientSendRate;
        this.sendPosition();
      }
    }
  }
  sendPosition() {
    var socket = this.socket;
    socket.emit('a');
    socket.emit('3', [
      this.id,
      this.x,
      this.y, -2.26,-1,0,0,null,0,0,0,0]);
    //
  }
  link(socket) {
    this.socket = socket;
    socket.once('error', err => {
      console.log(err);
      this.destroy();
    });
    socket.on('2', angle => this.aimAngle = angle);
    socket.on('3', angle => this.movement = angle);
    socket.once('disconnect', () => this.destroy());
    socket.emit('id', {
      teams: this.server.clans
    });
    socket.on('1', data => {
      this.name = data.name.length > 15 || !data.name ? 'unknown' : data.name;
      this.spawn();
      socket.emit('1', this.id);

    });
  }
  destroy() {
    this.kill();
    this.server.remove(this.id);
    this.socket.disconnect();
  }
  kill() {
    this.alive = false;
    this.x = 0;
    this.y = 0;
    this.slowDown();
  }
  slowDown() {
    this.vx = 0;
    this.vy = 0;
  }
  spawn() {
    this.alive = true;
    this.x = randInt(0, this.config.mapScale);
    this.y = randInt(0, this.config.mapScale);
    this.slowDown();
    var socket = this.socket
    socket.emit("2",[socket.id,this.id,this.name,this.x,this.y,0,100,100,35,0],true);
  }
}
class Server {
  constructor(config) {
    this.config = config;
    this.players = Array(config.maxPlayers).fill(null);
    this.lastRun = Date.now();
    this.clans = [];
    setInterval(() => this.update(), config.serverUpdateRate);
  }
  remove(sid) {
    this.players[sid] = null;
  }
  update() {
    var now = Date.now();
    var delta = now - this.lastRun;
    this.lastRun = now;
    for (var i of this.players) {
      if (i != null) {
        i.update(delta);
      }
    }
  }
  handle(socket) {
    for (var i = 0; i < 50; i++) {
      if (this.players[i] == null) {
        var player = new Player(this, i);
        player.link(socket);
        this.players[i] = player;
        break;
      }
    }
  }
}

var app = new Server({
  mapScale: 14400,
  maxPlayers: 50,
  clientSendRate: 5,
  serverUpdateRate: 9,
  playerDecel: 0.993,
  playerSpeed: 0.0016
});

for (var i = 5000; i <= 5010; i++) {
  io(i).on('connection', socket => app.handle(socket));
}
/*
var teams = [];
var sockets = [];
  io(i).on('connection', function (socket) {
    var emit = (...arg) => {
      try {
        socket.broadcast.emit(...arg);
        socket.emit(...arg);
      } catch (e) {
        sockets.forEach(a => a.emit(...arg));
      }
    };
    sockets.push(socket);
    socket.on('1', function (data) {
      var id = 27;
      var name = data.name.length > 15 ? 'unknown' : data.name;
      socket.emit('id', {
        "teams": teams
      });
      socket.emit('1', id);
      socket.emit('mm', 0);
      socket.emit('3', []);
      var x = randInt(0, config.mapScale);
      var y = randInt(0, config.mapScale);
      var userteam = null;
      var last = 0;
      socket.emit("2", ["a3Pm5dMzeKOjc5gvAJEF", id, name, x, 7200, 0, 100, 100, 35, data.skin], true);
      socket.emit("5", [27, data.name, 9001]); //[5,"RIP",31988,45,"KADEJO503",23404,34,"winter wolf",4821,28,"Godenot",4500,33,"Arena Closer",3000,32,"LightTheif",2940,6,"CarlosKoS-_-16",2800,4,"GD desconhecido",2635,35,"jack black GD",2357,19,"AMIGO BOM",1623])
      socket.emit("6", [
        29, 3115.1, 13592.9, 0, 109.2, 1, null, -1,
        353, 4103, 13436, 0, 80, 2, null, -1,
        339, 2498, 14155, 0, 90, 2, null, -1
      ]);
      socket.emit("8", 2.6, 28);
      socket.on("ch", data => {
        emit("ch", id, data);
      });
      socket.on("8", data => {
        console.log('CLAN CREATE REQUEST:', data);
        teams.push([{
          "sid": data,
          "owner": id
        }]);
        var userteam = data;
        emit("st", data, true);
      });
      socket.on("3", function (data) {
        if (data == null) {
          vx = vy = 0;
          return;
        } else if (x < 0 || x > config.mapScale || y < 0 || y > config.mapScale) {
          x = 7200;
          y = 7200;
        }
        if (y >= 6840 && y <= 7560) {
          x += 0.44;
        }
        var speed = y > 2400 ? 60 * 0.8 : 60;
        vx = Math.cos(data) * speed;
        vy = Math.sin(data) * speed;
        x += vx;
        y += vy;
        socket.emit('a');
        socket.emit("3", [id, x, y, -2.26, -1, 0, 0, null, 0, 0, 0, 0]);
      });
      socket.on("14", function (data) {
        console.log('ping!!!', arguments);
      });
    });
  });
}
*/
repl.start({
  eval: (a, _c, _f, cb) => {
    try {
      cb(null, eval(a)); // jshint ignore: line
    } catch (e) {
      cb(e);
    }
  }
});
<|endoftext|>


<commit_before>//Copyright (c) 2012, Carnegie Mellon University.
//All rights reserved.
//
//Redistribution and use in source and binary forms, with or without
//modification, are permitted provided that the following conditions
//are met:
//1. Redistributions of source code must retain the above copyright
//   notice, this list of conditions and the following disclaimer.
//2. Redistributions in binary form must reproduce the above copyright
//   notice, this list of conditions and the following disclaimer in the
//   documentation and/or other materials provided with the distribution.
//3. Neither the name of the University nor the names of its contributors
//   may be used to endorse or promote products derived from this software
//   without specific prior written permission.

/**
 * Data sources
 * @author ishafer
 */

function FakeData(freq,lbl) {
    this.freq = freq;
    this.dta = [];
    var TSTART = 1319000400699;
    var TEND = 1321708772699;
    var NPOINTS = 500;
    for (var i = 0; i < NPOINTS; ++i) {
        var t = (i+0.0)/NPOINTS*(TEND-TSTART)+TSTART;
        this.dta.push([t, Math.sin(t*freq)+1]);
    }
    this.lbl = lbl;
}
FakeData.prototype.get = function(tmin, tmax, next) {
    next([{
        color: "rgb(255,0,0)",
        data: this.dta,
        label: this.lbl
    }]);
}

/**
 * @param ids a list of timeseries identifiers
 * each id looks like: {node:"cirrus1", metric:"cpu.usage.average"}
 */
function BoundData(savename, ids) {
    this.ids = ids;
    this.series = [];
    this.savename = savename;
}
BoundData.prototype.pull = function(tmin, tmax, next) {
    this.series = [];
    var self = this;
    $.each(self.ids, function(i, id) {
        var rparams;
        $.getJSON("data", rparams = {
            savename: self.savename,
            tmin: tmin,
            tmax: tmax,
            node: id.node,
            metric: id.metric
        }, function(res) {
            var dta = res.dta;
            for (var i = 0; i < dta.length; ++i) {
                //multiply by 1000 for milliseconds for server data
                dta[i][0] *= 1000;
                //nuke the NaNs
                if (dta[i][1] != dta[i][1]) {
                    dta[i][1] = "NaN";
                }
            }
            self.series[i] = {
                data: dta,
                label: id.node + " " + id.metric
            };
            if (self.series.length == self.ids.length) {
                next();
            }
        });
        //console.log("Request params were ", rparams);
    });
}
BoundData.cmpfn = function(a,b) {
    return a[0] - b[0];
};
BoundData.prototype.get = function(tmin, tmax, next) {
    var self = this;
    //check to see if we need to reload
    var dxmin = 0, hbnd = 0;
    if (self.series[0]) {
        var dxmin = binSearch(self.series[0].data, [tmin, 0], BoundData.cmpfn);
        var dxmax = binSearch(self.series[0].data, [tmax, 0], BoundData.cmpfn);
        var hbnd = -self.series[0].data.length + 2;
    }
    if (dxmin == 0 || dxmax < hbnd) {
        //query is out of range
        this.pull(tmin, tmax, function() {
            next(self.series);
        });
    } else {
        //use cache
        next(self.series);
    }
}

/**
 * Binary search. Return -(pos_to_insert) if value is not found.
 * @param buf buffer to search
 * @param val x (time) value to search for
 */
var binSearch = function(arr, val, comparator) {
    if (!comparator) {
        comparator = function(a,b) {
            return a - b;
        };
    }
    var lo = 0;
    var hi = arr.length-1;
    var cmp = 0;
    var mid = 0;
    while (lo <= hi) {
        mid = Math.floor(lo/2 + hi/2);
        cmp = comparator(val, arr[mid]);
        if (cmp < 0) {
            hi = mid - 1;
        } else if (cmp > 0) {
            lo = mid + 1;
        } else {
            return mid;
        }
    }

    return -mid;
}<commit_msg>That won't work.<commit_after>//Copyright (c) 2012, Carnegie Mellon University.
//All rights reserved.
//
//Redistribution and use in source and binary forms, with or without
//modification, are permitted provided that the following conditions
//are met:
//1. Redistributions of source code must retain the above copyright
//   notice, this list of conditions and the following disclaimer.
//2. Redistributions in binary form must reproduce the above copyright
//   notice, this list of conditions and the following disclaimer in the
//   documentation and/or other materials provided with the distribution.
//3. Neither the name of the University nor the names of its contributors
//   may be used to endorse or promote products derived from this software
//   without specific prior written permission.

/**
 * Data sources
 * @author ishafer
 */

function FakeData(freq,lbl) {
    this.freq = freq;
    this.dta = [];
    var TSTART = 1319000400699;
    var TEND = 1321708772699;
    var NPOINTS = 500;
    for (var i = 0; i < NPOINTS; ++i) {
        var t = (i+0.0)/NPOINTS*(TEND-TSTART)+TSTART;
        this.dta.push([t, Math.sin(t*freq)+1]);
    }
    this.lbl = lbl;
}
FakeData.prototype.get = function(tmin, tmax, next) {
    next([{
        color: "rgb(255,0,0)",
        data: this.dta,
        label: this.lbl
    }]);
}

/**
 * @param ids a list of timeseries identifiers
 * each id looks like: {node:"cirrus1", metric:"cpu.usage.average"}
 */
function BoundData(savename, ids) {
    this.ids = ids;
    this.series = [];
    this.savename = savename;
}
BoundData.prototype.pull = function(tmin, tmax, next) {
    this.series = [];
    var self = this;

    var nloaded = 0;
    $.each(self.ids, function(sdx, id) {
        var rparams;
        $.getJSON("data", rparams = {
            savename: self.savename,
            tmin: tmin,
            tmax: tmax,
            node: id.node,
            metric: id.metric
        }, function(res) {
            var dta = res.dta;
            for (var i = 0; i < dta.length; ++i) {
                //multiply by 1000 for milliseconds for server data
                dta[i][0] *= 1000;
                //nuke the NaNs
                if (dta[i][1] != dta[i][1]) {
                    dta[i][1] = "NaN";
                }
            }
            self.series[sdx] = {
                data: dta,
                label: id.node + " " + id.metric
            };
            ++nloaded;
            if (nloaded == self.ids.length) {
                next();
            }
        });
        //console.log("Request params were ", rparams);
    });
}
BoundData.cmpfn = function(a,b) {
    return a[0] - b[0];
};
BoundData.prototype.get = function(tmin, tmax, next) {
    var self = this;
    //check to see if we need to reload
    var dxmin = 0, hbnd = 0;
    if (self.series[0]) {
        var dxmin = binSearch(self.series[0].data, [tmin, 0], BoundData.cmpfn);
        var dxmax = binSearch(self.series[0].data, [tmax, 0], BoundData.cmpfn);
        var hbnd = -self.series[0].data.length + 2;
    }
    if (dxmin == 0 || dxmax < hbnd) {
        //query is out of range
        this.pull(tmin, tmax, function() {
            next(self.series);
        });
    } else {
        //use cache
        next(self.series);
    }
}

/**
 * Binary search. Return -(pos_to_insert) if value is not found.
 * @param buf buffer to search
 * @param val x (time) value to search for
 */
var binSearch = function(arr, val, comparator) {
    if (!comparator) {
        comparator = function(a,b) {
            return a - b;
        };
    }
    var lo = 0;
    var hi = arr.length-1;
    var cmp = 0;
    var mid = 0;
    while (lo <= hi) {
        mid = Math.floor(lo/2 + hi/2);
        cmp = comparator(val, arr[mid]);
        if (cmp < 0) {
            hi = mid - 1;
        } else if (cmp > 0) {
            lo = mid + 1;
        } else {
            return mid;
        }
    }

    return -mid;
}<|endoftext|>


<commit_before>'use strict';

const statsHelpers = require('../../support/statsHelpers'),
forEach = require('lodash.foreach');

const METRIC_NAMES = ['transferSize', 'contentSize', 'requests'];
const CONTENT_TYPES = ['html', 'javascript', 'css', 'image', 'font'];

module.exports = {
  stats: {},
  addToAggregate(pageSummary) {
    let stats = this.stats;
    pageSummary.forEach(function(summary) {
      // stats for the whole page
      METRIC_NAMES.forEach(function(metric) {
        statsHelpers.pushStats(stats, metric, summary[metric]);
      });

      // stats for each content type
      CONTENT_TYPES.forEach(function(contentType) {
        if (summary.contentTypes[contentType]) {
          METRIC_NAMES.forEach(function(metric) {
            statsHelpers.pushStats(stats, contentType + '.' + metric, summary.contentTypes[contentType][metric]);
          });
        } else {
          METRIC_NAMES.forEach(function(metric) {
            statsHelpers.pushStats(stats, contentType + '.' + metric, 0);
          });
        }
      });
    })

  },
  summarize() {
    return Object.keys(this.stats).reduce((summary, name) => {
      if (METRIC_NAMES.indexOf(name) > -1) {
        statsHelpers.setStatsSummary(summary, name, this.stats[name])
      } else {
        const data = {};
        forEach(this.stats[name], (stats, metric) => {
          statsHelpers.setStatsSummary(data, metric, stats)
        });
        summary[name] = data;
      }
      return summary;
    }, {});
  }
};
<commit_msg>collect 1st/3rd party on the summary #915<commit_after>'use strict';

const statsHelpers = require('../../support/statsHelpers'),
forEach = require('lodash.foreach');

const METRIC_NAMES = ['transferSize', 'contentSize', 'requests'];
const CONTENT_TYPES = ['html', 'javascript', 'css', 'image', 'font'];

module.exports = {
  stats: {},
  addToAggregate(pageSummary) {
    let stats = this.stats;
    pageSummary.forEach(function(summary) {
      // stats for the whole page
      METRIC_NAMES.forEach(function(metric) {
        statsHelpers.pushStats(stats, metric, summary[metric]);
      });

      // stats for each content type
      CONTENT_TYPES.forEach(function(contentType) {
        if (summary.contentTypes[contentType]) {
          METRIC_NAMES.forEach(function(metric) {
            statsHelpers.pushStats(stats, contentType + '.' + metric, summary.contentTypes[contentType][metric]);
          });
        } else {
          METRIC_NAMES.forEach(function(metric) {
            statsHelpers.pushStats(stats, contentType + '.' + metric, 0);
          });
        }
      });

      // extras for firstParty vs third
      if (summary.firstParty.requests) {
          METRIC_NAMES.forEach(function(metric) {
            statsHelpers.pushStats(stats, 'firstParty'+ '.' + metric, summary.firstParty[metric]);
            statsHelpers.pushStats(stats, 'thirdParty' + '.' + metric, summary.thirdParty[metric]);
          })
      }
    })

  },
  summarize() {
    return Object.keys(this.stats).reduce((summary, name) => {
      if (METRIC_NAMES.indexOf(name) > -1) {
        statsHelpers.setStatsSummary(summary, name, this.stats[name])
      } else {
        const data = {};
        forEach(this.stats[name], (stats, metric) => {
          statsHelpers.setStatsSummary(data, metric, stats)
        });
        summary[name] = data;
      }
      return summary;
    }, {});
  }
};
<|endoftext|>


<commit_before>import ListSelection from './list-selection';

const COPY = {};

export default class FilePatchSelection {
  constructor(hunks) {
    if (hunks._copy !== COPY) {
      // Initialize a new selection
      this.mode = 'hunk';

      this.hunksByLine = new Map();
      const lines = [];
      for (const hunk of hunks) {
        for (const line of hunk.lines) {
          lines.push(line);
          this.hunksByLine.set(line, hunk);
        }
      }

      this.hunksSelection = new ListSelection({items: hunks});
      // TODO: investigate if there's a better fix for line.isChanged error after discarding
      this.linesSelection = new ListSelection({
        items: lines,
        isItemSelectable: line => {
          if (line === undefined) {
            console.log(this.linesSelection);
            debugger;
          }
          return line.isChanged();
        },
      });
      this.resolveNextUpdatePromise = () => {};
    } else {
      // Copy from options. *Only* reachable from the copy() method because no other module has visibility to
      // the COPY object without shenanigans.
      const options = hunks;

      this.mode = options.mode;
      this.hunksSelection = options.hunksSelection;
      this.linesSelection = options.linesSelection;
      this.resolveNextUpdatePromise = options.resolveNextUpdatePromise;
      this.hunksByLine = options.hunksByLine;
    }
  }

  copy(options = {}) {
    const mode = options.mode || this.mode;
    const hunksSelection = options.hunksSelection || this.hunksSelection.copy();
    const linesSelection = options.linesSelection || this.linesSelection.copy();

    let hunksByLine = null;
    if (options.hunks) {
      // Update hunks
      const oldHunks = this.hunksSelection.getItems();
      const newHunks = options.hunks;

      let wasChanged = false;
      if (newHunks.length !== oldHunks.length) {
        wasChanged = true;
      } else {
        for (let i = 0; i < oldHunks.length; i++) {
          if (oldHunks[i] !== newHunks[i]) {
            wasChanged = true;
            break;
          }
        }
      }

      // Update hunks, preserving selection index
      hunksSelection.setItems(newHunks);

      const oldLines = this.linesSelection.getItems();
      const newLines = [];

      hunksByLine = new Map();
      for (const hunk of newHunks) {
        for (const line of hunk.lines) {
          newLines.push(line);
          hunksByLine.set(line, hunk);
        }
      }

      // Update lines, preserving selection index in *changed* lines
      let newSelectedLine;
      if (oldLines.length > 0 && newLines.length > 0) {
        const oldSelectionStartIndex = this.linesSelection.getMostRecentSelectionStartIndex();
        let changedLineCount = 0;
        for (let i = 0; i < oldSelectionStartIndex; i++) {
          if (oldLines[i].isChanged()) { changedLineCount++; }
        }

        for (let i = 0; i < newLines.length; i++) {
          const line = newLines[i];
          if (line.isChanged()) {
            newSelectedLine = line;
            if (changedLineCount === 0) { break; }
            changedLineCount--;
          }
        }
      }

      linesSelection.setItems(newLines);
      if (newSelectedLine) { linesSelection.selectItem(newSelectedLine); }
      if (wasChanged) { this.resolveNextUpdatePromise(); }
    } else {
      // Hunks are unchanged. Don't recompute hunksByLine.
      hunksByLine = this.hunksByLine;
    }

    return new FilePatchSelection({
      _copy: COPY,
      mode,
      hunksSelection,
      linesSelection,
      hunksByLine,
      resolveNextUpdatePromise: options.resolveNextUpdatePromise || this.resolveNextUpdatePromise,
    });
  }

  toggleMode() {
    if (this.mode === 'hunk') {
      const firstLineOfSelectedHunk = this.getHeadHunk().lines[0];
      const selection = this.selectLine(firstLineOfSelectedHunk);
      if (!firstLineOfSelectedHunk.isChanged()) {
        return selection.selectNextLine();
      } else {
        return selection;
      }
    } else {
      const selectedLine = this.getHeadLine();
      const hunkContainingSelectedLine = this.hunksByLine.get(selectedLine);
      return this.selectHunk(hunkContainingSelectedLine);
    }
  }

  getMode() {
    return this.mode;
  }

  selectNext(preserveTail = false) {
    if (this.mode === 'hunk') {
      return this.selectNextHunk(preserveTail);
    } else {
      return this.selectNextLine(preserveTail);
    }
  }

  selectPrevious(preserveTail = false) {
    if (this.mode === 'hunk') {
      return this.selectPreviousHunk(preserveTail);
    } else {
      return this.selectPreviousLine(preserveTail);
    }
  }

  selectAll() {
    if (this.mode === 'hunk') {
      return this.selectAllHunks();
    } else {
      return this.selectAllLines();
    }
  }

  selectFirst(preserveTail) {
    if (this.mode === 'hunk') {
      return this.selectFirstHunk(preserveTail);
    } else {
      return this.selectFirstLine(preserveTail);
    }
  }

  selectLast(preserveTail) {
    if (this.mode === 'hunk') {
      return this.selectLastHunk(preserveTail);
    } else {
      return this.selectLastLine(preserveTail);
    }
  }

  selectHunk(hunk, preserveTail = false) {
    const hunksSelection = this.hunksSelection.copy();
    hunksSelection.selectItem(hunk, preserveTail);

    return this.copy({mode: 'hunk', hunksSelection});
  }

  addOrSubtractHunkSelection(hunk) {
    const hunksSelection = this.hunksSelection.copy();
    hunksSelection.addOrSubtractSelection(hunk);

    return this.copy({mode: 'hunk', hunksSelection});
  }

  selectAllHunks() {
    const hunksSelection = this.hunksSelection.copy();
    hunksSelection.selectAllItems();

    return this.copy({mode: 'hunk', hunksSelection});
  }

  selectFirstHunk(preserveTail) {
    const hunksSelection = this.hunksSelection.copy();
    hunksSelection.selectFirstItem(preserveTail);

    return this.copy({mode: 'hunk', hunksSelection});
  }

  selectLastHunk(preserveTail) {
    const hunksSelection = this.hunksSelection.copy();
    hunksSelection.selectLastItem(preserveTail);

    return this.copy({mode: 'hunk', hunksSelection});
  }

  jumpToNextHunk() {
    const next = this.selectNextHunk();
    return next.getMode() !== this.mode ? next.toggleMode() : next;
  }

  jumpToPreviousHunk() {
    const next = this.selectPreviousHunk();
    return next.getMode() !== this.mode ? next.toggleMode() : next;
  }

  selectNextHunk(preserveTail) {
    const hunksSelection = this.hunksSelection.copy();
    hunksSelection.selectNextItem(preserveTail);

    return this.copy({mode: 'hunk', hunksSelection});
  }

  selectPreviousHunk(preserveTail) {
    const hunksSelection = this.hunksSelection.copy();
    hunksSelection.selectPreviousItem(preserveTail);

    return this.copy({mode: 'hunk', hunksSelection});
  }

  getSelectedHunks() {
    if (this.mode === 'line') {
      const selectedHunks = new Set();
      const selectedLines = this.getSelectedLines();
      selectedLines.forEach(line => selectedHunks.add(this.hunksByLine.get(line)));
      return selectedHunks;
    } else {
      return this.hunksSelection.getSelectedItems();
    }
  }

  getHeadHunk() {
    return this.mode === 'hunk' ? this.hunksSelection.getHeadItem() : null;
  }

  selectLine(line, preserveTail = false) {
    const linesSelection = this.linesSelection.copy();
    linesSelection.selectItem(line, preserveTail);
    return this.copy({mode: 'line', linesSelection});
  }

  addOrSubtractLineSelection(line) {
    const linesSelection = this.linesSelection.copy();
    linesSelection.addOrSubtractSelection(line);
    return this.copy({mode: 'line', linesSelection});
  }

  selectAllLines(preserveTail) {
    const linesSelection = this.linesSelection.copy();
    linesSelection.selectAllItems(preserveTail);
    return this.copy({mode: 'line', linesSelection});
  }

  selectFirstLine(preserveTail) {
    const linesSelection = this.linesSelection.copy();
    linesSelection.selectFirstItem(preserveTail);
    return this.copy({mode: 'line', linesSelection});
  }

  selectLastLine(preserveTail) {
    const linesSelection = this.linesSelection.copy();
    linesSelection.selectLastItem(preserveTail);
    return this.copy({mode: 'line', linesSelection});
  }

  selectNextLine(preserveTail = false) {
    const linesSelection = this.linesSelection.copy();
    linesSelection.selectNextItem(preserveTail);
    return this.copy({mode: 'line', linesSelection});
  }

  selectPreviousLine(preserveTail = false) {
    const linesSelection = this.linesSelection.copy();
    linesSelection.selectPreviousItem(preserveTail);
    return this.copy({mode: 'line', linesSelection});
  }

  getSelectedLines() {
    if (this.mode === 'hunk') {
      const selectedLines = new Set();
      this.getSelectedHunks().forEach(hunk => {
        for (const line of hunk.lines) {
          if (line.isChanged()) { selectedLines.add(line); }
        }
      });
      return selectedLines;
    } else {
      return this.linesSelection.getSelectedItems();
    }
  }

  getHeadLine() {
    return this.mode === 'line' ? this.linesSelection.getHeadItem() : null;
  }

  updateHunks(newHunks) {
    return this.copy({hunks: newHunks});
  }

  coalesce() {
    const hunksSelection = this.hunksSelection.copy();
    const linesSelection = this.linesSelection.copy();

    hunksSelection.coalesce();
    linesSelection.coalesce();

    return this.copy({hunksSelection, linesSelection});
  }

  getNextUpdatePromise() {
    return new Promise((resolve, reject) => {
      this.resolveNextUpdatePromise = resolve;
    });
  }

  getLineSelectionTailIndex() {
    return this.linesSelection.getTailIndex();
  }

  goToDiffLine(lineNumber) {
    const lines = this.linesSelection.getItems();

    let closestLine;
    let closestLineDistance = Infinity;

    for (let i = 0; i < lines.length; i++) {
      const line = lines[i];
      if (!this.linesSelection.isItemSelectable(line)) { continue; }
      if (line.newLineNumber === lineNumber) {
        return this.selectLine(line);
      } else {
        const newDistance = Math.abs(line.newLineNumber - lineNumber);
        if (newDistance < closestLineDistance) {
          closestLineDistance = newDistance;
          closestLine = line;
        } else {
          return this.selectLine(closestLine);
        }
      }
    }

    return this.selectLine(closestLine);
  }
}
<commit_msg>Actual quick fix for error when line is undefined<commit_after>import ListSelection from './list-selection';

const COPY = {};

export default class FilePatchSelection {
  constructor(hunks) {
    if (hunks._copy !== COPY) {
      // Initialize a new selection
      this.mode = 'hunk';

      this.hunksByLine = new Map();
      const lines = [];
      for (const hunk of hunks) {
        for (const line of hunk.lines) {
          lines.push(line);
          this.hunksByLine.set(line, hunk);
        }
      }

      this.hunksSelection = new ListSelection({items: hunks});
      // TODO: investigate if there's a better fix for line.isChanged error after discarding
      this.linesSelection = new ListSelection({
        items: lines,
        isItemSelectable: line => line && line.isChanged(),
      });
      this.resolveNextUpdatePromise = () => {};
    } else {
      // Copy from options. *Only* reachable from the copy() method because no other module has visibility to
      // the COPY object without shenanigans.
      const options = hunks;

      this.mode = options.mode;
      this.hunksSelection = options.hunksSelection;
      this.linesSelection = options.linesSelection;
      this.resolveNextUpdatePromise = options.resolveNextUpdatePromise;
      this.hunksByLine = options.hunksByLine;
    }
  }

  copy(options = {}) {
    const mode = options.mode || this.mode;
    const hunksSelection = options.hunksSelection || this.hunksSelection.copy();
    const linesSelection = options.linesSelection || this.linesSelection.copy();

    let hunksByLine = null;
    if (options.hunks) {
      // Update hunks
      const oldHunks = this.hunksSelection.getItems();
      const newHunks = options.hunks;

      let wasChanged = false;
      if (newHunks.length !== oldHunks.length) {
        wasChanged = true;
      } else {
        for (let i = 0; i < oldHunks.length; i++) {
          if (oldHunks[i] !== newHunks[i]) {
            wasChanged = true;
            break;
          }
        }
      }

      // Update hunks, preserving selection index
      hunksSelection.setItems(newHunks);

      const oldLines = this.linesSelection.getItems();
      const newLines = [];

      hunksByLine = new Map();
      for (const hunk of newHunks) {
        for (const line of hunk.lines) {
          newLines.push(line);
          hunksByLine.set(line, hunk);
        }
      }

      // Update lines, preserving selection index in *changed* lines
      let newSelectedLine;
      if (oldLines.length > 0 && newLines.length > 0) {
        const oldSelectionStartIndex = this.linesSelection.getMostRecentSelectionStartIndex();
        let changedLineCount = 0;
        for (let i = 0; i < oldSelectionStartIndex; i++) {
          if (oldLines[i].isChanged()) { changedLineCount++; }
        }

        for (let i = 0; i < newLines.length; i++) {
          const line = newLines[i];
          if (line.isChanged()) {
            newSelectedLine = line;
            if (changedLineCount === 0) { break; }
            changedLineCount--;
          }
        }
      }

      linesSelection.setItems(newLines);
      if (newSelectedLine) { linesSelection.selectItem(newSelectedLine); }
      if (wasChanged) { this.resolveNextUpdatePromise(); }
    } else {
      // Hunks are unchanged. Don't recompute hunksByLine.
      hunksByLine = this.hunksByLine;
    }

    return new FilePatchSelection({
      _copy: COPY,
      mode,
      hunksSelection,
      linesSelection,
      hunksByLine,
      resolveNextUpdatePromise: options.resolveNextUpdatePromise || this.resolveNextUpdatePromise,
    });
  }

  toggleMode() {
    if (this.mode === 'hunk') {
      const firstLineOfSelectedHunk = this.getHeadHunk().lines[0];
      const selection = this.selectLine(firstLineOfSelectedHunk);
      if (!firstLineOfSelectedHunk.isChanged()) {
        return selection.selectNextLine();
      } else {
        return selection;
      }
    } else {
      const selectedLine = this.getHeadLine();
      const hunkContainingSelectedLine = this.hunksByLine.get(selectedLine);
      return this.selectHunk(hunkContainingSelectedLine);
    }
  }

  getMode() {
    return this.mode;
  }

  selectNext(preserveTail = false) {
    if (this.mode === 'hunk') {
      return this.selectNextHunk(preserveTail);
    } else {
      return this.selectNextLine(preserveTail);
    }
  }

  selectPrevious(preserveTail = false) {
    if (this.mode === 'hunk') {
      return this.selectPreviousHunk(preserveTail);
    } else {
      return this.selectPreviousLine(preserveTail);
    }
  }

  selectAll() {
    if (this.mode === 'hunk') {
      return this.selectAllHunks();
    } else {
      return this.selectAllLines();
    }
  }

  selectFirst(preserveTail) {
    if (this.mode === 'hunk') {
      return this.selectFirstHunk(preserveTail);
    } else {
      return this.selectFirstLine(preserveTail);
    }
  }

  selectLast(preserveTail) {
    if (this.mode === 'hunk') {
      return this.selectLastHunk(preserveTail);
    } else {
      return this.selectLastLine(preserveTail);
    }
  }

  selectHunk(hunk, preserveTail = false) {
    const hunksSelection = this.hunksSelection.copy();
    hunksSelection.selectItem(hunk, preserveTail);

    return this.copy({mode: 'hunk', hunksSelection});
  }

  addOrSubtractHunkSelection(hunk) {
    const hunksSelection = this.hunksSelection.copy();
    hunksSelection.addOrSubtractSelection(hunk);

    return this.copy({mode: 'hunk', hunksSelection});
  }

  selectAllHunks() {
    const hunksSelection = this.hunksSelection.copy();
    hunksSelection.selectAllItems();

    return this.copy({mode: 'hunk', hunksSelection});
  }

  selectFirstHunk(preserveTail) {
    const hunksSelection = this.hunksSelection.copy();
    hunksSelection.selectFirstItem(preserveTail);

    return this.copy({mode: 'hunk', hunksSelection});
  }

  selectLastHunk(preserveTail) {
    const hunksSelection = this.hunksSelection.copy();
    hunksSelection.selectLastItem(preserveTail);

    return this.copy({mode: 'hunk', hunksSelection});
  }

  jumpToNextHunk() {
    const next = this.selectNextHunk();
    return next.getMode() !== this.mode ? next.toggleMode() : next;
  }

  jumpToPreviousHunk() {
    const next = this.selectPreviousHunk();
    return next.getMode() !== this.mode ? next.toggleMode() : next;
  }

  selectNextHunk(preserveTail) {
    const hunksSelection = this.hunksSelection.copy();
    hunksSelection.selectNextItem(preserveTail);

    return this.copy({mode: 'hunk', hunksSelection});
  }

  selectPreviousHunk(preserveTail) {
    const hunksSelection = this.hunksSelection.copy();
    hunksSelection.selectPreviousItem(preserveTail);

    return this.copy({mode: 'hunk', hunksSelection});
  }

  getSelectedHunks() {
    if (this.mode === 'line') {
      const selectedHunks = new Set();
      const selectedLines = this.getSelectedLines();
      selectedLines.forEach(line => selectedHunks.add(this.hunksByLine.get(line)));
      return selectedHunks;
    } else {
      return this.hunksSelection.getSelectedItems();
    }
  }

  getHeadHunk() {
    return this.mode === 'hunk' ? this.hunksSelection.getHeadItem() : null;
  }

  selectLine(line, preserveTail = false) {
    const linesSelection = this.linesSelection.copy();
    linesSelection.selectItem(line, preserveTail);
    return this.copy({mode: 'line', linesSelection});
  }

  addOrSubtractLineSelection(line) {
    const linesSelection = this.linesSelection.copy();
    linesSelection.addOrSubtractSelection(line);
    return this.copy({mode: 'line', linesSelection});
  }

  selectAllLines(preserveTail) {
    const linesSelection = this.linesSelection.copy();
    linesSelection.selectAllItems(preserveTail);
    return this.copy({mode: 'line', linesSelection});
  }

  selectFirstLine(preserveTail) {
    const linesSelection = this.linesSelection.copy();
    linesSelection.selectFirstItem(preserveTail);
    return this.copy({mode: 'line', linesSelection});
  }

  selectLastLine(preserveTail) {
    const linesSelection = this.linesSelection.copy();
    linesSelection.selectLastItem(preserveTail);
    return this.copy({mode: 'line', linesSelection});
  }

  selectNextLine(preserveTail = false) {
    const linesSelection = this.linesSelection.copy();
    linesSelection.selectNextItem(preserveTail);
    return this.copy({mode: 'line', linesSelection});
  }

  selectPreviousLine(preserveTail = false) {
    const linesSelection = this.linesSelection.copy();
    linesSelection.selectPreviousItem(preserveTail);
    return this.copy({mode: 'line', linesSelection});
  }

  getSelectedLines() {
    if (this.mode === 'hunk') {
      const selectedLines = new Set();
      this.getSelectedHunks().forEach(hunk => {
        for (const line of hunk.lines) {
          if (line.isChanged()) { selectedLines.add(line); }
        }
      });
      return selectedLines;
    } else {
      return this.linesSelection.getSelectedItems();
    }
  }

  getHeadLine() {
    return this.mode === 'line' ? this.linesSelection.getHeadItem() : null;
  }

  updateHunks(newHunks) {
    return this.copy({hunks: newHunks});
  }

  coalesce() {
    const hunksSelection = this.hunksSelection.copy();
    const linesSelection = this.linesSelection.copy();

    hunksSelection.coalesce();
    linesSelection.coalesce();

    return this.copy({hunksSelection, linesSelection});
  }

  getNextUpdatePromise() {
    return new Promise((resolve, reject) => {
      this.resolveNextUpdatePromise = resolve;
    });
  }

  getLineSelectionTailIndex() {
    return this.linesSelection.getTailIndex();
  }

  goToDiffLine(lineNumber) {
    const lines = this.linesSelection.getItems();

    let closestLine;
    let closestLineDistance = Infinity;

    for (let i = 0; i < lines.length; i++) {
      const line = lines[i];
      if (!this.linesSelection.isItemSelectable(line)) { continue; }
      if (line.newLineNumber === lineNumber) {
        return this.selectLine(line);
      } else {
        const newDistance = Math.abs(line.newLineNumber - lineNumber);
        if (newDistance < closestLineDistance) {
          closestLineDistance = newDistance;
          closestLine = line;
        } else {
          return this.selectLine(closestLine);
        }
      }
    }

    return this.selectLine(closestLine);
  }
}
<|endoftext|>


<commit_before>try {
    if (!Nabu) throw "Nabu Manager not loaded";
} catch (e) {
    throw "Nabu Manager not loaded";
}

if (typeof Nabu.UI === 'undefined') {
    Nabu.UI = function() {};
}

Nabu.UI.Table = function(object, params)
{
    this.events = new Nabu.EventPool();
    this.container = object;
    this.params = params;
    this.currentPage = 1;

    try {
        this.table = $(object).find('table').get(0);
    } catch (e) {
        this.table = null;
    }

    this.init();
};

Nabu.UI.Table.prototype = {

    addEventListener: function(event)
    {
        this.events.addEventListener(event);
    },

    removeEventListener: function(event)
    {
        this.events.removeEventListener(event);
    },

    init: function()
    {
        this.initUI();
        this.initPager();
        this.initEditButtonStyle();
        this.initSelectableCheckbox();
        this.initToolbar();
    },

    initUI: function()
    {
        var Self = this;

        $(window).resize(
            function()
            {
                var body_widths =
                    $(Self.container)
                        .find('tbody tr[data-type="row"]:first')
                        .children()
                        .map(
                            function() {
                                return $(this).width();
                            }
                        )
                        .get()
                ;
                $(Self.container)
                    .find('thead tr')
                    .children()
                    .each(
                        function (i, v) {
                            $(v).width(body_widths[i]);
                        }
                    )
                ;
            }
        ).resize();
    },

    initPager: function()
    {
        var Self = this;

        $(this.container).find('.table-pager').remove();
        if (this.params.tablePager && this.params.tableSize > 0) {
            var rows = $(this.container).find('tbody > tr:not([data-type="empty"])');
            var buttons = Math.ceil(rows.length / this.params.tableSize);
            var from_page = this.currentPage > 2 ? this.currentPage - 2 : 1;
            var to_page = from_page + 4 > buttons ? buttons : from_page + 4;
            from_page = to_page - from_page < 5 ? (to_page > 4 ? to_page - 4 : 1) : from_page;
            if (buttons > 1) {
                var html =
                    "
" +
                        "" +
                            "
" +
                                "
«
";
                for (var i = from_page; i <= to_page; i++) {
                    html +=     "
" + i + "
";
                }
                html +=         "
»
" +
                            "
" +
                        "" +
                    "
"
                ;
                $(this.container).append(html);

                pager = $(this.container).find('.table-pager');
                if (this.currentPage > 1) {
                    pager.find('li:first-child > a')
                        .on('click',
                            function(e) {
                                e.preventDefault();
                                if (Self.currentPage > 1) {
                                    Self.currentPage--;
                                    Self.initPager();
                                }
                            }
                        )
                    ;
                }
                if (this.currentPage < buttons) {
                    pager.find('li:last-child > a')
                        .on('click',
                            function(e) {
                                e.preventDefault();
                                if (Self.currentPage < buttons) {
                                    Self.currentPage++;
                                    Self.initPager();
                                }
                            }
                        )
                    ;
                }
                pager.find('li[data-page]')
                    .on('click',
                        function(e) {
                            e.preventDefault();
                            Self.currentPage = $(this).data('page');
                            Self.initPager();
                        }
                    )
                ;

                for (var i = 0; i < rows.length; i++) {
                    var page = Math.ceil((i + 1) / this.params.tableSize);
                    if (page === this.currentPage) {
                        $(rows[i]).removeClass('hide');
                    } else {
                        $(rows[i]).addClass('hide');
                    }
                }
            } else {
                rows.removeAttr('hide');
            }
        }
    },

    initEditButtonStyle: function()
    {
        var Self = this;

        if (this.params.editButton === 'button') {
            $(this.container).find('.btn-editor')
                .unbind('click')
                .on('click', function(e) {
                    return Self.doEditButtonClick(e);
                })
            ;
        } else if (this.params.editButton === 'line') {
            $(this.container).find('tr[data-id]')
                .unbind('click')
                .on('click', function(e) {
                    return Self.doEditButtonClick(e);
                })
            ;
        }
    },

    initSelectableCheckbox: function()
    {
        var Self = this;

        $(this.container).find('thead th[data-toggle="table-selectable"] input[type="checkbox"]')
            .unbind('click')
            .on('click', function(e) {
                return Self.doSelectableHeadCheckbox(e);
            })
        ;

        $(this.container).find('tbody td[data-toggle="table-selectable"] input[type="checkbox"]')
            .unbind('click')
            .on('click', function(e) {
                return Self.doSelectableBodyCheckbox(e);
            })
        ;
    },

    initToolbar: function()
    {
        var Self = this;

        $(this.container).find('.table-toolbar .btn')
            .on('click', function(e) {
                var data = $(this).data();
                if (data.action) {
                    Self.events.fireEvent('onToolbarClick', Self, {
                        action: data.action,
                        selection: Self.getSelectedItems()
                    });
                }
                if (!data.toggle) {
                    if (this.tagName.toLowerCase() === 'a') {
                        var sel = Self.getSelectedItems();
                        if (sel.length ===1 && data.href) {
                            $(this).attr('href', $.sprintf(data.href, sel[0]));
                            return true;
                        }
                    }
                    e.preventDefault();
                    return false;
                } else {
                    return true;
                }

            })
        ;

        this.enableToolbarButtons();
    },

    enableToolbarButtons: function()
    {
        var toolbar = $(this.container).find('.table-toolbar');
        if (toolbar.length > 0) {
            var selected = this.getSelectedItems();
            toolbar.find('.btn').attr('disabled', 'disabled');
            if (selected !== null) {
                if (selected.length === 1) {
                    toolbar.find('[data-apply="all"], [data-apply="single"], [data-apply="multiple"]')
                           .removeAttr('disabled');
                } else if (selected.length > 1) {
                    toolbar.find('[data-apply="all"], [data-apply="multiple"]')
                           .removeAttr('disabled');
                }
            } else {
                toolbar.find('[data-apply="all"]')
                       .removeAttr('disabled');
            }
        }
    },

    doEditButtonClick: function(e)
    {
        var id = false;

        if (this.params.editButton === 'line') {
            id = $(e.currentTarget).data('id');
        } else if (this.params.editButton === 'button') {
            id = $(e.currentTarget).closest('[data-id]').data('id');
        } else {
            throw Exception('Invalid editButton value [' + id + ']');
        }

        this.editor(id);
    },

    editor: function(id)
    {
        var Self = this;
        var is_new = ((typeof id) === 'undefined');

        var target = is_new
                   ? $.sprintf(this.params.editor, '')
                   : $.sprintf(this.params.editor, id)
        ;

        if (this.params.editorMode === 'ajax' && this.params.editorContainer.length > 0) {

            if (is_new) {
                var row_id =  'new_' + (new Date().getTime()) + '' + Math.floor(Math.random() * 900) + 100;
                this.hideEmptyMessage();
                this.appendRow(row_id);
                this.initSelectableCheckbox();
                this.initEditButtonStyle();
            } else {
                this.editRow(id);
            }

            var container = $("#" + this.params.editorContainer);
            if (container.length > 0) {
                container.find('[id^=' + this.params.editorContainer + '_]').addClass('hide');
            }

            var myst = container.find('.myst');
            myst.removeClass('hide');

            var current = is_new ? [] : container.find('[id=' + this.params.editorContainer + '_' + id + ']');
            if (current.length > 0) {
                var cont_id = Self.params.editorContainer + '_' + (is_new ? row_id : id);
                current.removeClass('hide');
                myst.addClass('hide');
                $(this.table).find('tr').removeClass('editing');
                $(this.table).find('tr[data-id="' + id + '"]').addClass('editing');
                Self.events.fireEvent('onShownEditor', Self, {
                    id: (is_new ? row_id : id),
                    container_id: cont_id
                });
            } else {
                nabu.loadLibrary('Ajax', function() {
                    var ajax = new Nabu.Ajax.Connector(target, 'GET');
                    ajax.addEventListener(new Nabu.Event({
                        onLoad: function(e) {
                            var cont_id = Self.params.editorContainer + '_' + (is_new ? row_id : id);
                            var div = document.createElement('DIV');
                            div.id = cont_id;
                            div.innerHTML = e.params.text;
                            container.append(div);
                            if (is_new) {
                                var multiform=container.find('form[data-multiform-part]');
                                multiform.each(function() {
                                    var part = $(this).data('multiform-part');
                                    $(this).attr('data-multiform-part', $.sprintf(part, row_id));
                                    $(this).data('multiform-part', $.sprintf(part, row_id));
                                });
                            }
                            $(Self.table).find('tr').removeClass('editing');
                            $(Self.table).find('tr[data-id="' + (is_new ? row_id : id) + '"]').addClass('editing');
                            Self.events.fireEvent('onLoadEditor', Self, {
                                id: (is_new ? row_id : id),
                                container_id: cont_id
                            });
                            myst.addClass('hide');
                        },
                        onError: function(e) {
                            myst.addClass('hide');
                        }
                    }));
                    ajax.execute();
                });
            }
        } else if (this.params.editorMode === 'page') {
            document.location = target;
        }

        return false;
    },

    appendRow: function(row_id)
    {
        if (this.table !== null) {
            var row = this.table.insertRow(this.table.rows.length);
            $(row).attr('data-id', row_id);
            if (this.params.editButton==='line') {
                $(row).addClass('btn-edit-line');
            }
            if (this.table.tHead.rows.length > 0) {
                var head_row = this.table.tHead.rows[0];
                for (var i=0; i < head_row.cells.length; i++) {
                    var head_cell = head_row.cells[i];
                    var cell = row.insertCell();
                    if (head_cell.hasAttribute('data-toggle') && head_cell.getAttribute('data-toggle') === 'table-selectable') {
                        cell.setAttribute('data-toggle', 'table-selectable');
                        cell.innerHTML = "<input type=\"checkbox\">";
                    }
                    if (head_cell.hasAttribute('class')) {
                        cell.setAttribute('class', head_cell.getAttribute('class'));
                    }
                    if (head_cell.hasAttribute('data-name')) {
                        cell.setAttribute('data-name', head_cell.getAttribute('data-name'));
                    }
                    if (head_cell.hasAttribute('data-align')) {
                        $(cell).addClass(head_cell.getAttribute('data-align'));
                    }
                    if (head_cell.hasAttribute('data-is-id') && head_cell.getAttribute('data-is-id') === 'true') {
                        $(cell).html('');
                    }
                }
            }
        }
    },

    editRow: function(row_id)
    {
        if (this.table !== null) {
            var body_row = $(this.table).find('tbody tr[data-id="' + row_id + '"]').get(0);
            var head_row = this.table.tHead.rows[0];
            if (body_row.cells.length === head_row.cells.length) {
                for (var i = 0; i < head_row.cells.length; i++) {
                    var head_cell = head_row.cells[i];
                    if ($(head_cell).data('is-id')) {
                        var body_cell = body_row.cells[i];
                        if ($(body_cell).find('.edited').length === 0) {
                            $('')
                                .insertBefore($(body_cell).children().get(0))
                            ;
                        }
                    }
                }
            }
        }
    },

    getCell: function(row_id, col_name)
    {
        if (this.table !== null) {
            return $(this.table).find('tbody tr[data-id="' + row_id + '"] td[data-name="' + col_name + '"]').html();
        }

        return null;
    },

    doSelectableHeadCheckbox: function(e)
    {
        e.stopPropagation();

        var checks = $(this.container).find('tbody td[data-toggle="table-selectable"] input[type="checkbox"]');
        var head = $(e.currentTarget);
        if (e.currentTarget.checked) {
            checks.prop('checked', true);
        } else {
            checks.prop('checked', false);
        }

        this.enableToolbarButtons();

        return true;
    },

    doSelectableBodyCheckbox: function(e)
    {
        e.stopPropagation();

        var head = $(this.container).find('thead th[data-toggle="table-selectable"] input[type="checkbox"]');
        var total = $(this.container).find('tbody td[data-toggle="table-selectable"] input[type="checkbox"]').length;
        var checks = $(this.container).find('tbody td[data-toggle="table-selectable"] input[type="checkbox"]:checked').length;

        head.prop('checked', (total === checks));

        this.enableToolbarButtons();

        return true;
    },

    getSelectedItems: function()
    {
        var selected = [];

        $(this.container)
            .find('tbody td[data-toggle="table-selectable"] input[type="checkbox"]:checked')
            .closest('tr')
            .each(function(e) {
                if ($(this).data('id')) {
                    selected.push($(this).data('id'));
                }
            })
        ;

        return selected.length > 0 ? selected : null;
    },

    connectForm: function(table_row_id, form_id)
    {
        var $form = $(form_id);
        if ($form.length === 1) {
            var form = $form.get(0);
            if (form.nabuForm) {
                var Self = this;
                form.nabuForm.addEventListener(new Nabu.Event({
                    onFieldChange: function(e, params) {
                        var head_cell = $(Self.table).find("thead th[data-name=\"" + e.params.field + "\"]");
                        if (head_cell.length === 1) {
                            var text = e.params.value;
                            var head_lookup = head_cell.data('lookup');
                            if ((typeof head_lookup) !== "undefined") {
                                text = head_lookup[e.params.value];
                            }
                            $(Self.table)
                                .find("tbody tr[data-id=\"" + table_row_id + "\"] td[data-name=\"" + e.params.field + "\"]")
                                .text(text)
                            ;
                        }
                    }
                }));
            } else {
                console.log("Nabu.Form object not found");
            }
        } else if ($form.length > 1) {
            console.log("More than one forms found with same identifier [" + form_id + "]");
        }
    },

    hideEmptyMessage: function()
    {
        $(this.container).find('tbody > tr[data-type="empty"]').addClass('hide');
    }
};

nabu.registerLibrary('Table', ['Event', 'Ajax']);
<commit_msg>Allow clone line capability<commit_after>try {
    if (!Nabu) throw "Nabu Manager not loaded";
} catch (e) {
    throw "Nabu Manager not loaded";
}

if (typeof Nabu.UI === 'undefined') {
    Nabu.UI = function() {};
}

Nabu.UI.Table = function(object, params)
{
    this.events = new Nabu.EventPool();
    this.container = object;
    this.params = params;
    this.currentPage = 1;

    try {
        this.table = $(object).find('table').get(0);
    } catch (e) {
        this.table = null;
    }

    this.init();
};

Nabu.UI.Table.prototype = {

    addEventListener: function(event)
    {
        this.events.addEventListener(event);
    },

    removeEventListener: function(event)
    {
        this.events.removeEventListener(event);
    },

    init: function()
    {
        this.initUI();
        this.initPager();
        this.initEditButtonStyle();
        this.initSelectableCheckbox();
        this.initToolbar();
    },

    initUI: function()
    {
        var Self = this;

        $(window).resize(
            function()
            {
                var body_widths =
                    $(Self.container)
                        .find('tbody tr[data-type="row"]:first')
                        .children()
                        .map(
                            function() {
                                return $(this).width();
                            }
                        )
                        .get()
                ;
                $(Self.container)
                    .find('thead tr')
                    .children()
                    .each(
                        function (i, v) {
                            $(v).width(body_widths[i]);
                        }
                    )
                ;
            }
        ).resize();
    },

    initPager: function()
    {
        var Self = this;

        $(this.container).find('.table-pager').remove();
        if (this.params.tablePager && this.params.tableSize > 0) {
            var rows = $(this.container).find('tbody > tr:not([data-type="empty"])');
            var buttons = Math.ceil(rows.length / this.params.tableSize);
            var from_page = this.currentPage > 2 ? this.currentPage - 2 : 1;
            var to_page = from_page + 4 > buttons ? buttons : from_page + 4;
            from_page = to_page - from_page < 5 ? (to_page > 4 ? to_page - 4 : 1) : from_page;
            if (buttons > 1) {
                var html =
                    "
" +
                        "" +
                            "
" +
                                "
«
";
                for (var i = from_page; i <= to_page; i++) {
                    html +=     "
" + i + "
";
                }
                html +=         "
»
" +
                            "
" +
                        "" +
                    "
"
                ;
                $(this.container).append(html);

                pager = $(this.container).find('.table-pager');
                if (this.currentPage > 1) {
                    pager.find('li:first-child > a')
                        .on('click',
                            function(e) {
                                e.preventDefault();
                                if (Self.currentPage > 1) {
                                    Self.currentPage--;
                                    Self.initPager();
                                }
                            }
                        )
                    ;
                }
                if (this.currentPage < buttons) {
                    pager.find('li:last-child > a')
                        .on('click',
                            function(e) {
                                e.preventDefault();
                                if (Self.currentPage < buttons) {
                                    Self.currentPage++;
                                    Self.initPager();
                                }
                            }
                        )
                    ;
                }
                pager.find('li[data-page]')
                    .on('click',
                        function(e) {
                            e.preventDefault();
                            Self.currentPage = $(this).data('page');
                            Self.initPager();
                        }
                    )
                ;

                for (var i = 0; i < rows.length; i++) {
                    var page = Math.ceil((i + 1) / this.params.tableSize);
                    if (page === this.currentPage) {
                        $(rows[i]).removeClass('hide');
                    } else {
                        $(rows[i]).addClass('hide');
                    }
                }
            } else {
                rows.removeAttr('hide');
            }
        }
    },

    initEditButtonStyle: function()
    {
        var Self = this;

        if (this.params.editButton === 'button') {
            $(this.container).find('.btn-editor')
                .unbind('click')
                .on('click', function(e) {
                    return Self.doEditButtonClick(e);
                })
            ;
        } else if (this.params.editButton === 'line') {
            $(this.container).find('tr[data-id]')
                .unbind('click')
                .on('click', function(e) {
                    return Self.doEditButtonClick(e);
                })
            ;
        }
    },

    initSelectableCheckbox: function()
    {
        var Self = this;

        $(this.container).find('thead th[data-toggle="table-selectable"] input[type="checkbox"]')
            .unbind('click')
            .on('click', function(e) {
                return Self.doSelectableHeadCheckbox(e);
            })
        ;

        $(this.container).find('tbody td[data-toggle="table-selectable"] input[type="checkbox"]')
            .unbind('click')
            .on('click', function(e) {
                return Self.doSelectableBodyCheckbox(e);
            })
        ;
    },

    initToolbar: function()
    {
        var Self = this;

        $(this.container).find('.table-toolbar .btn')
            .on('click', function(e) {
                var data = $(this).data();
                if (data.action) {
                    Self.events.fireEvent('onToolbarClick', Self, {
                        action: data.action,
                        selection: Self.getSelectedItems()
                    });
                }
                if (!data.toggle) {
                    if (this.tagName.toLowerCase() === 'a') {
                        var sel = Self.getSelectedItems();
                        if (sel.length ===1 && data.href) {
                            $(this).attr('href', $.sprintf(data.href, sel[0]));
                            return true;
                        }
                    }
                    e.preventDefault();
                    return false;
                } else {
                    return true;
                }

            })
        ;

        this.enableToolbarButtons();
    },

    enableToolbarButtons: function()
    {
        var toolbar = $(this.container).find('.table-toolbar');
        if (toolbar.length > 0) {
            var selected = this.getSelectedItems();
            toolbar.find('.btn').attr('disabled', 'disabled');
            if (selected !== null) {
                if (selected.length === 1) {
                    toolbar.find('[data-apply="all"], [data-apply="single"], [data-apply="multiple"]')
                           .removeAttr('disabled');
                } else if (selected.length > 1) {
                    toolbar.find('[data-apply="all"], [data-apply="multiple"]')
                           .removeAttr('disabled');
                }
            } else {
                toolbar.find('[data-apply="all"]')
                       .removeAttr('disabled');
            }
        }
    },

    doEditButtonClick: function(e)
    {
        var id = false;

        if (this.params.editButton === 'line') {
            id = $(e.currentTarget).data('id');
        } else if (this.params.editButton === 'button') {
            id = $(e.currentTarget).closest('[data-id]').data('id');
        } else {
            throw Exception('Invalid editButton value [' + id + ']');
        }

        this.editor(id);
    },

    editor: function(id, clone)
    {
        var Self = this;
        var is_new = ((typeof id) === 'undefined');
        var clone = ((typeof clone) !== 'undefined' && clone);

        var target = is_new
                   ? $.sprintf(this.params.editor, '')
                   : $.sprintf(this.params.editor, id)
        ;

        if (clone) {
            target += (target.indexOf('?') >= 0 ? '&' : '?') + 'clone';
        }

        if (this.params.editorMode === 'ajax' && this.params.editorContainer.length > 0) {

            if (is_new) {
                var row_id =  'new_' + (new Date().getTime()) + '' + Math.floor(Math.random() * 900) + 100;
                this.hideEmptyMessage();
                this.appendRow(row_id);
                this.initSelectableCheckbox();
                this.initEditButtonStyle();
            } else {
                this.editRow(id);
            }

            var container = $("#" + this.params.editorContainer);
            if (container.length > 0) {
                container.find('[id^=' + this.params.editorContainer + '_]').addClass('hide');
            }

            var myst = container.find('.myst');
            myst.removeClass('hide');

            var current = is_new ? [] : container.find('[id=' + this.params.editorContainer + '_' + id + ']');
            if (current.length > 0) {
                var cont_id = Self.params.editorContainer + '_' + (is_new ? row_id : id);
                current.removeClass('hide');
                myst.addClass('hide');
                $(this.table).find('tr').removeClass('editing');
                $(this.table).find('tr[data-id="' + id + '"]').addClass('editing');
                Self.events.fireEvent('onShownEditor', Self, {
                    id: (is_new ? row_id : id),
                    container_id: cont_id
                });
            } else {
                nabu.loadLibrary('Ajax', function() {
                    var ajax = new Nabu.Ajax.Connector(target, 'GET');
                    ajax.addEventListener(new Nabu.Event({
                        onLoad: function(e) {
                            var cont_id = Self.params.editorContainer + '_' + (is_new ? row_id : id);
                            var div = document.createElement('DIV');
                            div.id = cont_id;
                            div.innerHTML = e.params.text;
                            container.append(div);
                            if (is_new) {
                                var multiform=container.find('form[data-multiform-part]');
                                multiform.each(function() {
                                    var part = $(this).data('multiform-part');
                                    $(this).attr('data-multiform-part', $.sprintf(part, row_id));
                                    $(this).data('multiform-part', $.sprintf(part, row_id));
                                });
                            }
                            $(Self.table).find('tr').removeClass('editing');
                            $(Self.table).find('tr[data-id="' + (is_new ? row_id : id) + '"]').addClass('editing');
                            Self.events.fireEvent('onLoadEditor', Self, {
                                id: (is_new ? row_id : id),
                                container_id: cont_id
                            });
                            myst.addClass('hide');
                        },
                        onError: function(e) {
                            myst.addClass('hide');
                        }
                    }));
                    ajax.execute();
                });
            }
        } else if (this.params.editorMode === 'page') {
            document.location = target;
        }

        return false;
    },

    appendRow: function(row_id)
    {
        if (this.table !== null) {
            var row = this.table.insertRow(this.table.rows.length);
            $(row).attr('data-id', row_id);
            if (this.params.editButton==='line') {
                $(row).addClass('btn-edit-line');
            }
            if (this.table.tHead.rows.length > 0) {
                var head_row = this.table.tHead.rows[0];
                for (var i=0; i < head_row.cells.length; i++) {
                    var head_cell = head_row.cells[i];
                    var cell = row.insertCell();
                    if (head_cell.hasAttribute('data-toggle') && head_cell.getAttribute('data-toggle') === 'table-selectable') {
                        cell.setAttribute('data-toggle', 'table-selectable');
                        cell.innerHTML = "<input type=\"checkbox\">";
                    }
                    if (head_cell.hasAttribute('class')) {
                        cell.setAttribute('class', head_cell.getAttribute('class'));
                    }
                    if (head_cell.hasAttribute('data-name')) {
                        cell.setAttribute('data-name', head_cell.getAttribute('data-name'));
                    }
                    if (head_cell.hasAttribute('data-align')) {
                        $(cell).addClass(head_cell.getAttribute('data-align'));
                    }
                    if (head_cell.hasAttribute('data-is-id') && head_cell.getAttribute('data-is-id') === 'true') {
                        $(cell).html('');
                    }
                }
            }
        }
    },

    editRow: function(row_id)
    {
        if (this.table !== null) {
            var body_row = $(this.table).find('tbody tr[data-id="' + row_id + '"]').get(0);
            var head_row = this.table.tHead.rows[0];
            if (body_row.cells.length === head_row.cells.length) {
                for (var i = 0; i < head_row.cells.length; i++) {
                    var head_cell = head_row.cells[i];
                    if ($(head_cell).data('is-id')) {
                        var body_cell = body_row.cells[i];
                        if ($(body_cell).find('.edited').length === 0) {
                            $('')
                                .insertBefore($(body_cell).children().get(0))
                            ;
                        }
                    }
                }
            }
        }
    },

    getCell: function(row_id, col_name)
    {
        if (this.table !== null) {
            return $(this.table).find('tbody tr[data-id="' + row_id + '"] td[data-name="' + col_name + '"]').html();
        }

        return null;
    },

    doSelectableHeadCheckbox: function(e)
    {
        e.stopPropagation();

        var checks = $(this.container).find('tbody td[data-toggle="table-selectable"] input[type="checkbox"]');
        var head = $(e.currentTarget);
        if (e.currentTarget.checked) {
            checks.prop('checked', true);
        } else {
            checks.prop('checked', false);
        }

        this.enableToolbarButtons();

        return true;
    },

    doSelectableBodyCheckbox: function(e)
    {
        e.stopPropagation();

        var head = $(this.container).find('thead th[data-toggle="table-selectable"] input[type="checkbox"]');
        var total = $(this.container).find('tbody td[data-toggle="table-selectable"] input[type="checkbox"]').length;
        var checks = $(this.container).find('tbody td[data-toggle="table-selectable"] input[type="checkbox"]:checked').length;

        head.prop('checked', (total === checks));

        this.enableToolbarButtons();

        return true;
    },

    getSelectedItems: function()
    {
        var selected = [];

        $(this.container)
            .find('tbody td[data-toggle="table-selectable"] input[type="checkbox"]:checked')
            .closest('tr')
            .each(function(e) {
                if ($(this).data('id')) {
                    selected.push($(this).data('id'));
                }
            })
        ;

        return selected.length > 0 ? selected : null;
    },

    connectForm: function(table_row_id, form_id)
    {
        var $form = $(form_id);
        if ($form.length === 1) {
            var form = $form.get(0);
            if (form.nabuForm) {
                var Self = this;
                form.nabuForm.addEventListener(new Nabu.Event({
                    onFieldChange: function(e, params) {
                        var head_cell = $(Self.table).find("thead th[data-name=\"" + e.params.field + "\"]");
                        if (head_cell.length === 1) {
                            var text = e.params.value;
                            var head_lookup = head_cell.data('lookup');
                            if ((typeof head_lookup) !== "undefined") {
                                text = head_lookup[e.params.value];
                            }
                            $(Self.table)
                                .find("tbody tr[data-id=\"" + table_row_id + "\"] td[data-name=\"" + e.params.field + "\"]")
                                .text(text)
                            ;
                        }
                    }
                }));
            } else {
                console.log("Nabu.Form object not found");
            }
        } else if ($form.length > 1) {
            console.log("More than one forms found with same identifier [" + form_id + "]");
        }
    },

    hideEmptyMessage: function()
    {
        $(this.container).find('tbody > tr[data-type="empty"]').addClass('hide');
    }
};

nabu.registerLibrary('Table', ['Event', 'Ajax']);
<|endoftext|>


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

'use strict';

var fs = require('fs');
var path = require('path');
var program = require('commander');
var mkdirp = require('mkdirp');
var chalk = require('chalk');
var jade = require('jade');

var basename = path.basename;
var dirname = path.dirname;
var resolve = path.resolve;
var normalize = path.normalize;
var join = path.join;

// jade options

var options = {};

// options

program
  .version(
    'jade version: '     + require('jade/package.json').version + '\n' +
    'jade-cli version: ' + require(   './package.json').version
  )
  .usage('[options] [dir|file ...]')
  .option('-O, --obj <str|path>', 'JSON/JavaScript options object or file')
  .option('-o, --out <dir>', 'output the rendered HTML or compiled JavaScript to <dir>')
  .option('-p, --path <path>', 'filename used to resolve includes')
  .option('-P, --pretty', 'compile pretty html output')
  .option('-c, --client', 'compile function for client-side runtime.js')
  .option('-n, --name <str>', 'the name of the compiled template (requires --client)')
  .option('-D, --no-debug', 'compile without debugging (smaller functions)')
  .option('-w, --watch', 'watch files for changes and automatically re-render')
  .option('-E, --extension <ext>', 'specify the output file extension')
  .option('-H, --hierarchy', 'keep directory hierarchy when a directory is specified')
  .option('-s, --silent', 'do not output logs')
  .option('--name-after-file', 'name the template after the last section of the file path (requires --client and overriden by --name)')
  .option('--doctype <str>', 'specify the doctype on the command line (useful if it is not specified by the template)')

program.on('--help', function(){
  console.log('  Examples:');
  console.log('');
  console.log('    # Render all files in the `templates` directory:');
  console.log('    $ jade templates');
  console.log('');
  console.log('    # Create {foo,bar}.html:');
  console.log('    $ jade {foo,bar}.jade');
  console.log('');
  console.log('    # Using `jade` over standard input and output streams');
  console.log('    $ jade < my.jade > my.html');
  console.log('    $ echo \'h1 Jade!\' | jade');
  console.log('');
  console.log('    # Render all files in `foo` and `bar` directories to `/tmp`:');
  console.log('    $ jade foo bar --out /tmp');
  console.log('');
});

program.parse(process.argv);

// options given, parse them

if (program.obj) {
  options = parseObj(program.obj);
}

/**
 * Parse object either in `input` or in the file called `input`. The latter is
 * searched first.
 */
function parseObj (input) {
  var str;
  try {
    str = fs.readFileSync(program.obj, 'utf8');
  } catch (e) {
    str = program.obj;
  }
  try {
    return JSON.parse(str);
  } catch (e) {
    return eval('(' + str + ')');
  }
}

// --path

if (program.path) options.filename = program.path;

// --no-debug

options.compileDebug = program.debug;

// --client

options.client = program.client;

// --pretty

options.pretty = program.pretty;

// --watch

options.watch = program.watch;

// --name

if (typeof program.name === 'string') {
  options.name = program.name;
}

// --doctype

options.doctype = program.doctype;

// --hierarchy

if (program.hierarchy) {
  console.warn('`-H`, `--hierarchy` option is redundant now, and will be removed in jade-cli@1.0.0');
}

// --silent

var consoleLog = program.silent ? function() {} : console.log;

// left-over args are file paths

var files = program.args;

// object of reverse dependencies of a watched file, including itself if
// applicable

var watchList = {};

// function for rendering
var render = program.watch ? tryRender : renderFile;

// compile files

if (files.length) {
  consoleLog();
  if (options.watch) {
    process.on('SIGINT', function() {
      process.exit(1);
    });
  }
  files.forEach(function (file) {
    render(file);
  });
  process.on('exit', function () {
    consoleLog();
  });
// stdio
} else {
  stdin();
}

/**
 * Watch for changes on path
 *
 * Renders `base` if specified, otherwise renders `path`.
 */
function watchFile(path, base, rootPath) {
  path = normalize(path);

  var log = '  ' + chalk.gray('watching') + ' ' + chalk.cyan(path);
  if (!base) {
    base = path;
  } else {
    log += '\n    ' + chalk.gray('as a dependency of') + ' ';
    log += chalk.cyan(base);
  }

  if (watchList[path]) {
    if (watchList[path].indexOf(base) !== -1) return;
    consoleLog(log);
    watchList[path].push(base);
    return;
  }

  consoleLog(log);
  watchList[path] = [base];
  fs.watchFile(path, {persistent: true, interval: 200},
               function (curr, prev) {
    // File doesn't exist anymore. Keep watching.
    if (curr.mtime.getTime() === 0) return;
    // istanbul ignore if
    if (curr.mtime.getTime() === prev.mtime.getTime()) return;
    watchList[path].forEach(function(file) {
      tryRender(file, rootPath);
    });
  });
}

/**
 * Convert error to string
 */
function errorToString(e) {
  return e.stack || /* istanbul ignore next */ (e.message || e);
}

/**
 * Try to render `path`; if an exception is thrown it is printed to stderr and
 * otherwise ignored.
 *
 * This is used in watch mode.
 */
function tryRender(path, rootPath) {
  try {
    renderFile(path, rootPath);
  } catch (e) {
    // keep watching when error occured.
    console.error(errorToString(e));
  }
}

/**
 * Compile from stdin.
 */

function stdin() {
  var buf = '';
  process.stdin.setEncoding('utf8');
  process.stdin.on('data', function(chunk){ buf += chunk; });
  process.stdin.on('end', function(){
    var output;
    if (options.client) {
      output = jade.compileClient(buf, options);
    } else {
      var fn = jade.compile(buf, options);
      var output = fn(options);
    }
    process.stdout.write(output);
  }).resume();

  /* istanbul ignore next */
  process.on('SIGINT', function() {
    process.stdout.write('\n');
    process.stdin.emit('end');
    process.stdout.write('\n');
    process.stderr.write('In jade-cli@1.0.0, SIGINT will no longer signify end of input, and `jade` will exit immediately. If you are using a POSIX shell, this means that you will have to press Control-D (EOF) instead of Control-C.\n');
    process.exit();
  })
}

/**
 * Process the given path, compiling the jade files found.
 * Always walk the subdirectories.
 *
 * @param path      path of the file, might be relative
 * @param rootPath  path relative to the directory specified in the command
 */

function renderFile(path, rootPath) {
  var re = /\.jade$/;
  var stat = fs.lstatSync(path);
  // Found jade file/\.jade$/
  if (stat.isFile() && re.test(path)) {
    // Try to watch the file if needed. watchFile takes care of duplicates.
    if (options.watch) watchFile(path, null, rootPath);
    if (program.nameAfterFile) {
      options.name = getNameFromFileName(path);
    }
    var fn = options.client
           ? jade.compileFileClient(path, options)
           : jade.compileFile(path, options);
    if (options.watch && fn.dependencies) {
      // watch dependencies, and recompile the base
      fn.dependencies.forEach(function (dep) {
        watchFile(dep, path, rootPath);
      });
    }

    // --extension
    var extname;
    if (program.extension)   extname = '.' + program.extension;
    else if (options.client) extname = '.js';
    else                     extname = '.html';

    // path: foo.jade -> foo.<ext>
    path = path.replace(re, extname);
    if (program.out) {
      // prepend output directory
      if (rootPath) {
        // replace the rootPath of the resolved path with output directory
        path = resolve(path).replace(new RegExp('^' + resolve(rootPath)), '');
        path = join(program.out, path);
      } else {
        // old behavior or if no rootPath handling is needed
        path = join(program.out, basename(path));
      }
    }
    var dir = resolve(dirname(path));
    mkdirp.sync(dir);
    var output = options.client ? fn : fn(options);
    fs.writeFileSync(path, output);
    consoleLog('  ' + chalk.gray('rendered') + ' ' + chalk.cyan('%s'), normalize(path));
  // Found directory
  } else if (stat.isDirectory()) {
    var files = fs.readdirSync(path);
    files.map(function(filename) {
      return path + '/' + filename;
    }).forEach(function (file) {
      render(file, rootPath || path);
    });
  }
}

/**
 * Get a sensible name for a template function from a file path
 *
 * @param {String} filename
 * @returns {String}
 */
function getNameFromFileName(filename) {
  var file = basename(filename, '.jade');
  return file.toLowerCase().replace(/[^a-z0-9]+([a-z])/g, function (_, character) {
    return character.toUpperCase();
  }) + 'Template';
}
<commit_msg>Remove unused exit event handler<commit_after>#!/usr/bin/env node

'use strict';

var fs = require('fs');
var path = require('path');
var program = require('commander');
var mkdirp = require('mkdirp');
var chalk = require('chalk');
var jade = require('jade');

var basename = path.basename;
var dirname = path.dirname;
var resolve = path.resolve;
var normalize = path.normalize;
var join = path.join;

// jade options

var options = {};

// options

program
  .version(
    'jade version: '     + require('jade/package.json').version + '\n' +
    'jade-cli version: ' + require(   './package.json').version
  )
  .usage('[options] [dir|file ...]')
  .option('-O, --obj <str|path>', 'JSON/JavaScript options object or file')
  .option('-o, --out <dir>', 'output the rendered HTML or compiled JavaScript to <dir>')
  .option('-p, --path <path>', 'filename used to resolve includes')
  .option('-P, --pretty', 'compile pretty html output')
  .option('-c, --client', 'compile function for client-side runtime.js')
  .option('-n, --name <str>', 'the name of the compiled template (requires --client)')
  .option('-D, --no-debug', 'compile without debugging (smaller functions)')
  .option('-w, --watch', 'watch files for changes and automatically re-render')
  .option('-E, --extension <ext>', 'specify the output file extension')
  .option('-H, --hierarchy', 'keep directory hierarchy when a directory is specified')
  .option('-s, --silent', 'do not output logs')
  .option('--name-after-file', 'name the template after the last section of the file path (requires --client and overriden by --name)')
  .option('--doctype <str>', 'specify the doctype on the command line (useful if it is not specified by the template)')

program.on('--help', function(){
  console.log('  Examples:');
  console.log('');
  console.log('    # Render all files in the `templates` directory:');
  console.log('    $ jade templates');
  console.log('');
  console.log('    # Create {foo,bar}.html:');
  console.log('    $ jade {foo,bar}.jade');
  console.log('');
  console.log('    # Using `jade` over standard input and output streams');
  console.log('    $ jade < my.jade > my.html');
  console.log('    $ echo \'h1 Jade!\' | jade');
  console.log('');
  console.log('    # Render all files in `foo` and `bar` directories to `/tmp`:');
  console.log('    $ jade foo bar --out /tmp');
  console.log('');
});

program.parse(process.argv);

// options given, parse them

if (program.obj) {
  options = parseObj(program.obj);
}

/**
 * Parse object either in `input` or in the file called `input`. The latter is
 * searched first.
 */
function parseObj (input) {
  var str;
  try {
    str = fs.readFileSync(program.obj, 'utf8');
  } catch (e) {
    str = program.obj;
  }
  try {
    return JSON.parse(str);
  } catch (e) {
    return eval('(' + str + ')');
  }
}

// --path

if (program.path) options.filename = program.path;

// --no-debug

options.compileDebug = program.debug;

// --client

options.client = program.client;

// --pretty

options.pretty = program.pretty;

// --watch

options.watch = program.watch;

// --name

if (typeof program.name === 'string') {
  options.name = program.name;
}

// --doctype

options.doctype = program.doctype;

// --hierarchy

if (program.hierarchy) {
  console.warn('`-H`, `--hierarchy` option is redundant now, and will be removed in jade-cli@1.0.0');
}

// --silent

var consoleLog = program.silent ? function() {} : console.log;

// left-over args are file paths

var files = program.args;

// object of reverse dependencies of a watched file, including itself if
// applicable

var watchList = {};

// function for rendering
var render = program.watch ? tryRender : renderFile;

// compile files

if (files.length) {
  consoleLog();
  if (options.watch) {
    process.on('SIGINT', function() {
      process.exit(1);
    });
  }
  files.forEach(function (file) {
    render(file);
  });
// stdio
} else {
  stdin();
}

/**
 * Watch for changes on path
 *
 * Renders `base` if specified, otherwise renders `path`.
 */
function watchFile(path, base, rootPath) {
  path = normalize(path);

  var log = '  ' + chalk.gray('watching') + ' ' + chalk.cyan(path);
  if (!base) {
    base = path;
  } else {
    log += '\n    ' + chalk.gray('as a dependency of') + ' ';
    log += chalk.cyan(base);
  }

  if (watchList[path]) {
    if (watchList[path].indexOf(base) !== -1) return;
    consoleLog(log);
    watchList[path].push(base);
    return;
  }

  consoleLog(log);
  watchList[path] = [base];
  fs.watchFile(path, {persistent: true, interval: 200},
               function (curr, prev) {
    // File doesn't exist anymore. Keep watching.
    if (curr.mtime.getTime() === 0) return;
    // istanbul ignore if
    if (curr.mtime.getTime() === prev.mtime.getTime()) return;
    watchList[path].forEach(function(file) {
      tryRender(file, rootPath);
    });
  });
}

/**
 * Convert error to string
 */
function errorToString(e) {
  return e.stack || /* istanbul ignore next */ (e.message || e);
}

/**
 * Try to render `path`; if an exception is thrown it is printed to stderr and
 * otherwise ignored.
 *
 * This is used in watch mode.
 */
function tryRender(path, rootPath) {
  try {
    renderFile(path, rootPath);
  } catch (e) {
    // keep watching when error occured.
    console.error(errorToString(e));
  }
}

/**
 * Compile from stdin.
 */

function stdin() {
  var buf = '';
  process.stdin.setEncoding('utf8');
  process.stdin.on('data', function(chunk){ buf += chunk; });
  process.stdin.on('end', function(){
    var output;
    if (options.client) {
      output = jade.compileClient(buf, options);
    } else {
      var fn = jade.compile(buf, options);
      var output = fn(options);
    }
    process.stdout.write(output);
  }).resume();

  /* istanbul ignore next */
  process.on('SIGINT', function() {
    process.stdout.write('\n');
    process.stdin.emit('end');
    process.stdout.write('\n');
    process.stderr.write('In jade-cli@1.0.0, SIGINT will no longer signify end of input, and `jade` will exit immediately. If you are using a POSIX shell, this means that you will have to press Control-D (EOF) instead of Control-C.\n');
    process.exit();
  })
}

/**
 * Process the given path, compiling the jade files found.
 * Always walk the subdirectories.
 *
 * @param path      path of the file, might be relative
 * @param rootPath  path relative to the directory specified in the command
 */

function renderFile(path, rootPath) {
  var re = /\.jade$/;
  var stat = fs.lstatSync(path);
  // Found jade file/\.jade$/
  if (stat.isFile() && re.test(path)) {
    // Try to watch the file if needed. watchFile takes care of duplicates.
    if (options.watch) watchFile(path, null, rootPath);
    if (program.nameAfterFile) {
      options.name = getNameFromFileName(path);
    }
    var fn = options.client
           ? jade.compileFileClient(path, options)
           : jade.compileFile(path, options);
    if (options.watch && fn.dependencies) {
      // watch dependencies, and recompile the base
      fn.dependencies.forEach(function (dep) {
        watchFile(dep, path, rootPath);
      });
    }

    // --extension
    var extname;
    if (program.extension)   extname = '.' + program.extension;
    else if (options.client) extname = '.js';
    else                     extname = '.html';

    // path: foo.jade -> foo.<ext>
    path = path.replace(re, extname);
    if (program.out) {
      // prepend output directory
      if (rootPath) {
        // replace the rootPath of the resolved path with output directory
        path = resolve(path).replace(new RegExp('^' + resolve(rootPath)), '');
        path = join(program.out, path);
      } else {
        // old behavior or if no rootPath handling is needed
        path = join(program.out, basename(path));
      }
    }
    var dir = resolve(dirname(path));
    mkdirp.sync(dir);
    var output = options.client ? fn : fn(options);
    fs.writeFileSync(path, output);
    consoleLog('  ' + chalk.gray('rendered') + ' ' + chalk.cyan('%s'), normalize(path));
  // Found directory
  } else if (stat.isDirectory()) {
    var files = fs.readdirSync(path);
    files.map(function(filename) {
      return path + '/' + filename;
    }).forEach(function (file) {
      render(file, rootPath || path);
    });
  }
}

/**
 * Get a sensible name for a template function from a file path
 *
 * @param {String} filename
 * @returns {String}
 */
function getNameFromFileName(filename) {
  var file = basename(filename, '.jade');
  return file.toLowerCase().replace(/[^a-z0-9]+([a-z])/g, function (_, character) {
    return character.toUpperCase();
  }) + 'Template';
}
<|endoftext|>