<commit_before>dojo.provide("dojox.date.php");
dojo.require("dojo.date");

dojox.date.php.format = function(/*Date*/ date, /*String*/ format){
	// summary: Get a formatted string for a given date object
	var df = new dojox.date.php.DateFormat(date);
	return df.format(format);
}

dojox.date.php.DateFormat = function(date){
	this.date = date;
}
dojo.extend(dojox.date.php.DateFormat, {
	weekdays: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
	weekdays_3: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
	months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
	months_3: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
	monthdays: [31,28,31,30,31,30,31,31,30,31,30,31],

	format: function(/*String*/ format){
		// summary: Format the internal date object
		var parts = [];
		for(var i = 0; i < format.length; i++){
			var chr = format.charAt(i);
			if(typeof this[chr] == "function"){
				parts.push(this[chr]());
			}else{
				parts.push(chr);
			}
		}
		return parts.join("");
	},

	// Day

	d: function(){
		// summary: Day of the month, 2 digits with leading zeros
		var j = this.j();
		return (j.length == 1) ? "0" + j : j;
	},

	D: function(){
		// summary: A textual representation of a day, three letters
		return weekdays_3[this.date.getDay()];
	},

	j: function(){
		// summary: Day of the month without leading zeros
		return this.date.getDate() + "";
	},

	l: function(){
		// summary: A full textual representation of the day of the week
		return weekdays[this.date.getDay()];
	},

	N: function(){
		// summary: ISO-8601 numeric representation of the day of the week (added in PHP 5.1.0)
		var w = this.w();
		return (!w) ? 7 : w;
	},

	S: function(){
		// summary: English ordinal suffix for the day of the month, 2 characters
		switch(this.date.getDate()){
			case 11: case 12: case 13: return "th";
			case 1: case 21: case 31: return "st";
			case 2: case 22: return "nd";
			case 3: case 23: return "rd";
			default: return "th";
		}
	},

	w: function(){
		// summary: Numeric representation of the day of the week
		return this.date.getDay() + "";
	},

	z: function(){
		// summary: The day of the year (starting from 0)
		var millis = this.date.getTime() - new Date(this.date.getYear(), 1, 1).getTime();
		return Math.floor(millis/86400000) + "";
	},

	// Week

	W: function(){
		// summary: ISO-8601 week number of year, weeks starting on Monday (added in PHP 4.1.0)
		var week;
		var jan1_w = new Date(this.date.getYear(), 1, 1).getDay() + 1;
		var w = this.date.getDay() + 1;
		var z = parseInt(this.z());

		if(z <= (8 - jan1_w) && jan1_w > 4){
			if(jan1_w == 5 || (jan1_w == 6 && Boolean(this.L()))){
				week = 53;
			}else{
				week = 52;
			}
		}else{
			var i;
			if(Boolean(this.L())){
				i = 366;
			}else{
				i = 365;
			}
			if((i - z) < (4 - w)){
				week = 1;
			}else{
				var j = z + (7 - w) + (jan1_w - 1);
				week = j / 7;
				if(jan1_w > 4){
					--week;
				}
			}
		}

		return week;
	},

	// Month

	F: function(){
		// summary: A full textual representation of a month, such as January or March
		return this.months[this.date.getMonth()];
	},

	m: function(){
		// summary: Numeric representation of a month, with leading zeros
		var n = this.n();
		return (n.length == 1) ? "0" + n : n;
	},

	M: function(){
		// summary: A short textual representation of a month, three letters
		return months_3[this.date.getMonth()];
	},

	n: function(){
		// summary: Numeric representation of a month, without leading zeros
		return this.date.getMonth() + 1 + "";
	},

	t: function(){
		// summary: Number of days in the given month
		return (Boolean(this.L()) && this.date.getMonth() == 1) ? 29 : this.monthdays[this.getMonth()];
	},

	// Year

	L: function(){
		// summary: Whether it's a leap year
		return (dojo.date.isLeapYear(this.date)) ? "1" : "0";
	},

	o: function(){
		// summary:
		//		ISO-8601 year number. This has the same value as Y, except that if
		//		the ISO week number (W) belongs to the previous or next year, that year is used instead. (added in PHP 5.1.0)
		// TODO: Figure out what this means
	},

	Y: function(){
		// summary: A full numeric representation of a year, 4 digits
		return this.date.getFullYear() + "";
	},

	y: function(){
		// summary: A two digit representation of a year
		return this.date.getFullYear.substsring(2, 4);
	},

	// Time

	a: function(){
		// summary: Lowercase Ante meridiem and Post meridiem
		return this.date.getHours() >= 12 ? "pm" : "am";
	},

	b: function(){
		// summary: Uppercase Ante meridiem and Post meridiem
		return this.a().toUpperCase();
	},

	B: function(){
		// summary:
		//	Swatch Internet time
		//	A day is 1,000 beats. All time is measured from GMT + 1
		var off = this.date.getTimezoneOffset() + 60;
		var secs = (this.date.getHours() * 3600) + (this.date.getMinutes() * 60) + this.getSeconds() + (off * 60);
		var beat = Math.abs(Math.floor(secs / 86.4) % 1000) + "";
		while(beat.length <  2) beat = "0" + beat;
		return beat;
	},

	g: function(){
		// summary: 12-hour format of an hour without leading zeros
		return (this.date.getHours() > 12) ? this.date.getHours() - 12 + "" : this.date.getHours() + "";
	},

	G: function(){
		// summary: 24-hour format of an hour without leading zeros
		return this.date.getHours() + "";
	},

	h: function(){
		// summary: 12-hour format of an hour with leading zeros
		var g = this.g();
		return (g.length == 1) ? "0" + g : g;
	},

	H: function(){
		// summary: 24-hour format of an hour with leading zeros
		var G = this.G();
		return (G.length == 1) ? "0" + G : G;
	},

	i: function(){
		// summary: Minutes with leading zeros
		var mins = this.date.getMinutes() + "";
		return (mins.length == 1) ? "0" + mins : mins;
	},

	s: function(){
		// summary: Seconds, with leading zeros
		var secs = this.date.getSeconds() + "";
		return (secs.length == 1) ? "0" + secs : secs;
	},

	// Timezone

	e: function(){
		// summary: Timezone identifier (added in PHP 5.1.0)
		return dojo.date.getTimezoneName(this.date);
	},

	I: function(){
		// summary: Whether or not the date is in daylight saving time
		// TODO: Can dojo.date do this?
	},

	O: function(){
		// summary: Difference to Greenwich time (GMT) in hours
		var off = Math.abs(this.date.getTimeZoneOffset());
		var hours = Math.floor(off / 60) + "";
		var mins = (off % 60) + "";
		if(hours.length == 1) hours = "0" + hours;
		if(mins.length == 1) hours = "0" + mins;
		return ((this.date.getTimezoneOffset() < 0) ? "+" : "-") + hours + mins;
	},

	P: function(){
		// summary: Difference to Greenwich time (GMT) with colon between hours and minutes (added in PHP 5.1.3)
		var O = this.O();
		return O.substring(0, 2) + ":" + O.substring(2, 4);
	},

	T: function(){
		// summary: Timezone abbreviation

		// Guess...
		return this.e().substring(0, 3);
	},

	Z: function(){
		// summary:
		//		Timezone offset in seconds. The offset for timezones west of UTC is always negative,
		//		and for those east of UTC is always positive.
		return this.date.getTimeZoneOffset() * -60;
	},

	// Full Date/Time

	c: function(){
		// summary: ISO 8601 date (added in PHP 5)
		return this.Y() + "-" + this.m() + "-" + this.d() + "T" + this.h() + ":" + this.i() + ":" + this.s() + this.P();
	},

	r: function(){
		// summary: RFC 2822 formatted date
		return this.D() + ", " + this.d() + " " + this.M() + " " + this.Y() + " " + this.H() + ":" + this.i() + ":" + this.s() + " " + this.O();
	},

	U: function(){
		// summary: Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)
		return Math.floor(this.date.getTime() / 1000);
	}

});<commit_msg>fixes #3079. Fix getTimezone, weekdays, Jan 1 calculation, and support overrides<commit_after>dojo.provide("dojox.date.php");
dojo.require("dojo.date");

dojox.date.php.format = function(/*Date*/ date, /*String*/ format, /*Object?*/ overrides){
	// summary: Get a formatted string for a given date object
	var df = new dojox.date.php.DateFormat(date);
	return df.format(format, overrides);
}

dojox.date.php.DateFormat = function(/*Date*/ date){
	this.date = date;
}
dojo.extend(dojox.date.php.DateFormat, {
	weekdays: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
	weekdays_3: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
	months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
	months_3: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
	monthdays: [31,28,31,30,31,30,31,31,30,31,30,31],

	format: function(/*String*/ format, /*Object?*/ overrides){
		// summary: Format the internal date object
		var parts = [];
		for(var i = 0; i < format.length; i++){
			var chr = format.charAt(i);
			if(overrides && typeof overrides[chr] == "function"){
				parts.push(overrides[chr].call(this));
			}else if(typeof this[chr] == "function"){
				parts.push(this[chr]());
			}else{
				parts.push(chr);
			}
		}
		return parts.join("");
	},

	// Day

	d: function(){
		// summary: Day of the month, 2 digits with leading zeros
		var j = this.j();
		return (j.length == 1) ? "0" + j : j;
	},

	D: function(){
		// summary: A textual representation of a day, three letters
		return this.weekdays_3[this.date.getDay()];
	},

	j: function(){
		// summary: Day of the month without leading zeros
		return this.date.getDate() + "";
	},

	l: function(){
		// summary: A full textual representation of the day of the week
		return this.weekdays[this.date.getDay()];
	},

	N: function(){
		// summary: ISO-8601 numeric representation of the day of the week (added in PHP 5.1.0)
		var w = this.w();
		return (!w) ? 7 : w;
	},

	S: function(){
		// summary: English ordinal suffix for the day of the month, 2 characters
		switch(this.date.getDate()){
			case 11: case 12: case 13: return "th";
			case 1: case 21: case 31: return "st";
			case 2: case 22: return "nd";
			case 3: case 23: return "rd";
			default: return "th";
		}
	},

	w: function(){
		// summary: Numeric representation of the day of the week
		return this.date.getDay() + "";
	},

	z: function(){
		// summary: The day of the year (starting from 0)
		var millis = this.date.getTime() - new Date(this.date.getFullYear(), 0, 1).getTime();
		return Math.floor(millis/86400000) + "";
	},

	// Week

	W: function(){
		// summary: ISO-8601 week number of year, weeks starting on Monday (added in PHP 4.1.0)
		var week;
		var jan1_w = new Date(this.date.getFullYear(), 0, 1).getDay() + 1;
		var w = this.date.getDay() + 1;
		var z = parseInt(this.z());

		if(z <= (8 - jan1_w) && jan1_w > 4){
			var last_year = new Date(this.date.getFullYear() - 1, this.date.getMonth(), this.date.getDate());
			if(jan1_w == 5 || (jan1_w == 6 && dojo.date.isLeapYear(last_year))){
				week = 53;
			}else{
				week = 52;
			}
		}else{
			var i;
			if(Boolean(this.L())){
				i = 366;
			}else{
				i = 365;
			}
			if((i - z) < (4 - w)){
				week = 1;
			}else{
				var j = z + (7 - w) + (jan1_w - 1);
				week = Math.ceil(j / 7);
				if(jan1_w > 4){
					--week;
				}
			}
		}

		return week;
	},

	// Month

	F: function(){
		// summary: A full textual representation of a month, such as January or March
		return this.months[this.date.getMonth()];
	},

	m: function(){
		// summary: Numeric representation of a month, with leading zeros
		var n = this.n();
		return (n.length == 1) ? "0" + n : n;
	},

	M: function(){
		// summary: A short textual representation of a month, three letters
		return months_3[this.date.getMonth()];
	},

	n: function(){
		// summary: Numeric representation of a month, without leading zeros
		return this.date.getMonth() + 1 + "";
	},

	t: function(){
		// summary: Number of days in the given month
		return (Boolean(this.L()) && this.date.getMonth() == 1) ? 29 : this.monthdays[this.getMonth()];
	},

	// Year

	L: function(){
		// summary: Whether it's a leap year
		return (dojo.date.isLeapYear(this.date)) ? "1" : "0";
	},

	o: function(){
		// summary:
		//		ISO-8601 year number. This has the same value as Y, except that if
		//		the ISO week number (W) belongs to the previous or next year, that year is used instead. (added in PHP 5.1.0)
		// TODO: Figure out what this means
	},

	Y: function(){
		// summary: A full numeric representation of a year, 4 digits
		return this.date.getFullYear() + "";
	},

	y: function(){
		// summary: A two digit representation of a year
		return this.date.getFullYear.substsring(2, 4);
	},

	// Time

	a: function(){
		// summary: Lowercase Ante meridiem and Post meridiem
		return this.date.getHours() >= 12 ? "pm" : "am";
	},

	b: function(){
		// summary: Uppercase Ante meridiem and Post meridiem
		return this.a().toUpperCase();
	},

	B: function(){
		// summary:
		//	Swatch Internet time
		//	A day is 1,000 beats. All time is measured from GMT + 1
		var off = this.date.getTimezoneOffset() + 60;
		var secs = (this.date.getHours() * 3600) + (this.date.getMinutes() * 60) + this.getSeconds() + (off * 60);
		var beat = Math.abs(Math.floor(secs / 86.4) % 1000) + "";
		while(beat.length <  2) beat = "0" + beat;
		return beat;
	},

	g: function(){
		// summary: 12-hour format of an hour without leading zeros
		return (this.date.getHours() > 12) ? this.date.getHours() - 12 + "" : this.date.getHours() + "";
	},

	G: function(){
		// summary: 24-hour format of an hour without leading zeros
		return this.date.getHours() + "";
	},

	h: function(){
		// summary: 12-hour format of an hour with leading zeros
		var g = this.g();
		return (g.length == 1) ? "0" + g : g;
	},

	H: function(){
		// summary: 24-hour format of an hour with leading zeros
		var G = this.G();
		return (G.length == 1) ? "0" + G : G;
	},

	i: function(){
		// summary: Minutes with leading zeros
		var mins = this.date.getMinutes() + "";
		return (mins.length == 1) ? "0" + mins : mins;
	},

	s: function(){
		// summary: Seconds, with leading zeros
		var secs = this.date.getSeconds() + "";
		return (secs.length == 1) ? "0" + secs : secs;
	},

	// Timezone

	e: function(){
		// summary: Timezone identifier (added in PHP 5.1.0)
		return dojo.date.getTimezoneName(this.date);
	},

	I: function(){
		// summary: Whether or not the date is in daylight saving time
		// TODO: Can dojo.date do this?
	},

	O: function(){
		// summary: Difference to Greenwich time (GMT) in hours
		var off = Math.abs(this.date.getTimezoneOffset());
		var hours = Math.floor(off / 60) + "";
		var mins = (off % 60) + "";
		if(hours.length == 1) hours = "0" + hours;
		if(mins.length == 1) hours = "0" + mins;
		return ((this.date.getTimezoneOffset() < 0) ? "+" : "-") + hours + mins;
	},

	P: function(){
		// summary: Difference to Greenwich time (GMT) with colon between hours and minutes (added in PHP 5.1.3)
		var O = this.O();
		return O.substring(0, 2) + ":" + O.substring(2, 4);
	},

	T: function(){
		// summary: Timezone abbreviation

		// Guess...
		return this.e().substring(0, 3);
	},

	Z: function(){
		// summary:
		//		Timezone offset in seconds. The offset for timezones west of UTC is always negative,
		//		and for those east of UTC is always positive.
		return this.date.getTimezoneOffset() * -60;
	},

	// Full Date/Time

	c: function(){
		// summary: ISO 8601 date (added in PHP 5)
		return this.Y() + "-" + this.m() + "-" + this.d() + "T" + this.h() + ":" + this.i() + ":" + this.s() + this.P();
	},

	r: function(){
		// summary: RFC 2822 formatted date
		return this.D() + ", " + this.d() + " " + this.M() + " " + this.Y() + " " + this.H() + ":" + this.i() + ":" + this.s() + " " + this.O();
	},

	U: function(){
		// summary: Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)
		return Math.floor(this.date.getTime() / 1000);
	}

});<|endoftext|>


<commit_before>
var express = require('express')
  , _ = require('underscore')
  , Haiku = require('haiku')
  , express = require('express')
  , sys = require('sys')
  , colors = require('colors')
;

var Server = function(attrs){
  var server = this;

  server.haiku = new Haiku({ source: attrs.source });
  server.port = attrs.port;

  server.app = express.createServer();

  server.app.configure('development', function(){
    server.app.use(express.errorHandler({
      dumpExceptions: true, showStack: true
    }));
  });

  server.app.configure(function(){
    server.app.use(express.static(server.haiku.publicdir()));
  });

  server.app.get(/^\/(.*)/, function(req, res){
    var key = '/' + req.params[0]

    server.haiku.once('ready', function(){
      if (key === '/') key = '/index.html';

      console.log('partials:'.yellow.inverse);

      _.each(server.haiku.partials, function(value, k, list){
        var type = typeof value;
        console.log((('  ' + k + ': ').yellow) + type);
      });

      console.log('content:'.yellow.inverse);

      _.each(server.haiku.content, function(value, k, list){
        console.log((('  ' + k + ' ').magenta));
        value.get('content')();
      });

      var content = server.haiku.content[key];

      if (content){
        sys.puts('rendering: ' + content.url());

        content.render(function(err, html){
          if (err) { return res.send(err.message, 503); }

          return res.send(html);
        });
      } else res.send('not found', 404);
    });

    sys.puts('reading: '.yellow + server.haiku.get('source'));

    server.haiku.read();
  });
};

Server.prototype.run = function(){
  var server = this;

  // server.haiku.on('ready', function(){
  //   var message = 'haiku is running! //====>> http://localhost:' + server.port;
  //
  //   sys.puts(message)
  //
  //   console.log('partials:'.yellow.inverse);
  //
  //   _.each(server.haiku.partials, function(value, key, list){
  //     var type = typeof value;
  //     console.log((('  ' + key + ': ').yellow) + type);
  //   });
  //
  //   console.log('content:'.yellow.inverse);
  //
  //   _.each(server.haiku.content, function(value, key, list){
  //     console.log((('  ' + key + ' ').magenta));
  //     value.get('content')();
  //   });
  //
  //   server.app.listen(server.port);
  // });

  // sys.puts('reading: ' + this.haiku.get('source'))
  // sys.puts('...')
  //
  // server.haiku.read();

  var message = 'haiku is running! //====>> http://localhost:' + server.port;
  sys.puts(message)

  server.app.listen(server.port);
};

exports = module.exports = Server;
<commit_msg>debugged a non haiku issue, thought I got stuck in https://github.com/visionmedia/express/issues/561<commit_after>
var express = require('express')
  , _ = require('underscore')
  , Haiku = require('haiku')
  , express = require('express')
  , sys = require('sys')
  , colors = require('colors')
;

var Server = function(attrs){
  var server = this;

  server.source = attrs.source;

  server.haiku = new Haiku({ source: attrs.source });
  server.port = attrs.port;

  server.app = express.createServer();

  server.app.configure('development', function(){
    server.app.use(express.errorHandler({
      dumpExceptions: true, showStack: true
    }));
  });

  server.app.configure(function(){
    server.app.use(express.static(server.haiku.publicdir()));
  });

  server.app.get(/^\/(.*)/, function(req, res){
    var key = '/' + req.params[0];

    server.haiku.once('ready', function(){
      if (key === '/') key = '/index.html';

      var content = this.content[key];

      if (content){
        sys.puts('rendering: '.green + content.url());

        content.render(function(err, html){
          if (err) return res.send(err.message, 503);

          res.send(html);
        });
      }
      else res.send('not found', 404);
    });

    sys.puts('reading: '.yellow + server.haiku.get('source'));

    server.haiku.read();
  });
};

Server.prototype.run = function(){
  var server = this
    , message = 'haiku is running! //====>> http://localhost:' + server.port
  ;

  sys.puts(message)

  server.app.listen(server.port);
};

exports = module.exports = Server;
<|endoftext|>


<commit_before>import CodeMirror from 'codemirror';
import 'codemirror/addon/hint/anyword-hint';

const anywordHint = CodeMirror.hint.anyword;

CodeMirror.hint.javascript = (instance, options) => {

  const cursor = instance.getCursor();
  const token = instance.getTokenAt(cursor);
  const from = { line: cursor.line, ch: token.start };
  const to = { line: cursor.line, ch: cursor.ch };
  const empty = { list: [], from, to };

  if (/[\;\=\)\,]$|^\s*$|^\($/.test(token.string)) {
    return empty;
  }

  const result = anywordHint(instance, options) || empty;

  if (token.type === 'string') {
    const left = instance.getLine(cursor.line)
      .substr(0, cursor.ch)
      .substr(token.start + 1);
    const moduleNames = options.files
      .filter(file => file.moduleName.indexOf(left) === 0)
      .map(file => ({
        text: file.moduleName,
        from: { line: from.line, ch: from.ch + 1 },
      }));
    result.list = moduleNames.concat(result.list);
  }

  return result;

};
<commit_msg>Fix condition of completion<commit_after>import CodeMirror from 'codemirror';
import 'codemirror/addon/hint/anyword-hint';

const anywordHint = CodeMirror.hint.anyword;

CodeMirror.hint.javascript = (instance, options) => {

  const cursor = instance.getCursor();
  const token = instance.getTokenAt(cursor);
  const from = { line: cursor.line, ch: token.start };
  const to = { line: cursor.line, ch: cursor.ch };
  const empty = { list: [], from, to };

  if (!/[A-Za-z\.]$/.test(token.string)) {
    return empty;
  }

  const result = anywordHint(instance, options) || empty;

  if (token.type === 'string') {
    const left = instance.getLine(cursor.line)
      .substr(0, cursor.ch)
      .substr(token.start + 1);
    const moduleNames = options.files
      .filter(file => file.moduleName.indexOf(left) === 0)
      .map(file => ({
        text: file.moduleName,
        from: { line: from.line, ch: from.ch + 1 },
      }));
    result.list = moduleNames.concat(result.list);
  }

  return result;

};
<|endoftext|>


<commit_before>module.export = function (app) {

    var mongoose = app.mongoose;

    // Seed users
    var User = mongoose.model('User');
    User.createWithAttributes("appsfactory", "appsfactory", "admin", function (err, user) {
        if (err) return console.log("error, " + err);
    });

    // Seed clients
    var Client = mongoose.model('Client');
    Client.createWithClientIdAndSecret("client", "secret", function (err, client) {
        if (err) return console.log("error, " + err);
    });

}
<commit_msg>typo in 'exports'<commit_after>module.exports = function (app) {

    var mongoose = app.mongoose;

    // Seed users
    var User = mongoose.model('User');
    User.createWithAttributes("appsfactory", "appsfactory", "admin", function (err, user) {
        if (err) return console.log("error, " + err);
    });

    // Seed clients
    var Client = mongoose.model('Client');
    Client.createWithClientIdAndSecret("client", "secret", function (err, client) {
        if (err) return console.log("error, " + err);
    });

}
<|endoftext|>


<commit_before>var childProcess = require('child_process');
var spawn = childProcess.spawn;
var rimraf = require('rimraf');
var config = require('./config');
var services = {};
var debugServerChild = null;
var promisify = require('promisify-node');
var fs = promisify('fs');
var _ = require('lodash');
var forever = require('forever-monitor');
var unusedDebugPort = 5000;
var serviceIDs = 0;

function createDir(dir) {
  return fs.mkdir(dir).catch(function(err) {
    if (err && err.code === 'EEXIST') {
      return;
    }
    throw new Error(err);
  });
}

var ServiceManager = {
  serviceExists: function(serviceID) {
    return serviceID in services;
  },
  getServiceWithoutChild: function(service) {
    return _.omit(service, ['child']);
  },
  listProcesses: function() {
    return _.mapValues(services, this.getServiceWithoutChild);
  },
  getProcessInfo: function(serviceID) {
    if (!this.serviceExists(serviceID)) {
      return Promise.reject('Service #' + serviceID + ' does not exist.');
    }
    return fs.readFile(config.logsDir + '/' + serviceID + '/stdout.log', 'utf8').then(function(log) {
      return {
        service: this.getServiceWithoutChild(services[serviceID]),
        log: log
      };
    }.bind(this));
  },
  addService: function(entryPoint) {
    if (!fs.existsSync(config.servicesDir + '/' + entryPoint)) {
      return Promise.reject("Entry point doesn't exist");
    }
    var serviceID = serviceIDs++;
    services[serviceID] = {
      id: serviceID,
      status: 0,
      start: -1,
      kill: -1,
      child: null,
      debugPort: null,
      entryPoint: entryPoint
    };
    return createDir(config.logsDir).then(function() {
      return createDir(config.logsDir + '/' + serviceID);
    }).then(function() {
      return serviceID;
    });
  },
  getFilepath: function(serviceID) {
    return config.servicesDir + '/' + serviceID + '/index.js';
  },
  spawnProcess: function(serviceID) {
    if (!this.serviceExists(serviceID)) {
      throw new Error('Service #' + serviceID + ' not found.');
    }

    var service = services[serviceID];
    var serviceFile =  config.servicesDir + '/' + service.entryPoint;
    var logFile = config.logsDir + '/' + serviceID + '/stdout.log';
    fs.writeFile(logFile, '');
    var debugPort = unusedDebugPort++;
    var child = spawn('node', ['--debug=' + debugPort, serviceFile]);

    service.start = new Date().getTime();
    service.status = 1;
    service.child = child;
    service.debugPort = debugPort;

    services[serviceID] = service;

    child.stdout.on('data', function (data) {
      fs.appendFile(logFile, data.toString());
      console.log(child.pid, data.toString());
    });

    child.stderr.on('data', function (data) {
      fs.appendFile(logFile, data.toString());
      console.log(child.pid, data.toString());
    });

    child.on('close', function(exit_code) {
      var runningService = services[serviceID];
      if (runningService) {
        runningService.status = 0;
        runningService.kill = new Date().getTime();
      }

      console.log('Closed before stop: Closing code: ', exit_code);
    });

    return child.pid;
  },
  killProcess: function(serviceID) {
    var runningService = services[serviceID];
    if (runningService) {
      console.log('kill process');
      runningService.child.kill('SIGKILL');
      runningService.status = 0;
      runningService.kill = new Date().getTime();
    }
  },
  removeProcess: function(serviceID, cb) {
    var runningService = services[serviceID];
    if (runningService) {
      this.killProcess(serviceID);
      delete services[serviceID];
    }
  },
  startDebugServer: function() {
    if (debugServerChild) {
      console.log('Debug server was already started.');
      return;
    }
    console.log('Starting debug server on port ' +
                config.NODE_INSPECTOR_WEB_PORT+ '...');

    var inspectorPath = './node_modules/node-inspector/bin/inspector.js';

    var child = new (forever.Monitor)(
      inspectorPath,
      {
        max : 100,
        silent : true,
        args: [
          '--web-port', config.NODE_INSPECTOR_WEB_PORT,
          '--save-live-edit', 'true'
        ]
      }
    );

    child.start();

    debugServerChild = child;
    child.on('restart', function() {
      console.log("Debug server was restarted automatically");
    })

    child.on('stdout', function (data) {
      console.log(child.pid, data.toString());
    });

    child.on('stderr', function (data) {
      console.log(child.pid, data.toString());
    });

    child.on('exit', function(exit_code) {
      console.log('Closed before stop: Closing code: ', exit_code);
    });

    process.on('exit', function() {
      debugServerChild.kill('SIGKILL');
    });
  },
  shutdownDebugServer: function() {
    if (debugServerChild) {
      console.log('kill debug server');
      debugServerChild.kill('SIGKILL');
      debugServerChild = null;
    }
  }
};

module.exports = ServiceManager;<commit_msg>separate stdout and stderr<commit_after>var childProcess = require('child_process');
var spawn = childProcess.spawn;
var rimraf = require('rimraf');
var config = require('./config');
var services = {};
var debugServerChild = null;
var promisify = require('promisify-node');
var fs = promisify('fs');
var _ = require('lodash');
var forever = require('forever-monitor');
var unusedDebugPort = 5000;
var serviceIDs = 0;

function createDir(dir) {
  return fs.mkdir(dir).catch(function(err) {
    if (err && err.code === 'EEXIST') {
      return;
    }
    throw new Error(err);
  });
}

var ServiceManager = {
  serviceExists: function(serviceID) {
    return serviceID in services;
  },
  getServiceWithoutChild: function(service) {
    return _.omit(service, ['child']);
  },
  listProcesses: function() {
    return _.mapValues(services, this.getServiceWithoutChild);
  },
  getProcessInfo: function(serviceID) {
    if (!this.serviceExists(serviceID)) {
      return Promise.reject('Service #' + serviceID + ' does not exist.');
    }
    return Promise.all([
      fs.readFile(config.logsDir + '/' + serviceID + '/stdout.log', 'utf8'),
      fs.readFile(config.logsDir + '/' + serviceID + '/stderr.log', 'utf8')
    ]).then(function(logs) {
      return {
        service: this.getServiceWithoutChild(services[serviceID]),
        stdout: logs[0],
        stderr: logs[1]
      };
    }.bind(this));
  },
  addService: function(entryPoint) {
    if (!fs.existsSync(config.servicesDir + '/' + entryPoint)) {
      return Promise.reject("Entry point doesn't exist");
    }
    var serviceID = serviceIDs++;
    services[serviceID] = {
      id: serviceID,
      status: 0,
      start: -1,
      kill: -1,
      child: null,
      debugPort: null,
      entryPoint: entryPoint
    };
    return createDir(config.logsDir).then(function() {
      return createDir(config.logsDir + '/' + serviceID);
    }).then(function() {
      return serviceID;
    });
  },
  getFilepath: function(serviceID) {
    return config.servicesDir + '/' + serviceID + '/index.js';
  },
  spawnProcess: function(serviceID) {
    if (!this.serviceExists(serviceID)) {
      throw new Error('Service #' + serviceID + ' not found.');
    }

    var service = services[serviceID];
    var serviceFile =  config.servicesDir + '/' + service.entryPoint;
    var stdoutFile = config.logsDir + '/' + serviceID + '/stdout.log';
    var stderrFile = config.logsDir + '/' + serviceID + '/stderr.log';
    fs.writeFile(stdoutFile, '');
    fs.writeFile(stderrFile, '');
    var debugPort = unusedDebugPort++;
    var child = spawn('node', ['--debug=' + debugPort, serviceFile]);

    service.start = new Date().getTime();
    service.status = 1;
    service.child = child;
    service.debugPort = debugPort;

    services[serviceID] = service;

    child.stdout.on('data', function (data) {
      fs.appendFile(stdoutFile, data.toString());
      console.log(child.pid, data.toString());
    });

    child.stderr.on('data', function (data) {
      fs.appendFile(stderrFile, data.toString());
      console.log(child.pid, data.toString());
    });

    child.on('close', function(exit_code) {
      var runningService = services[serviceID];
      if (runningService) {
        runningService.status = 0;
        runningService.kill = new Date().getTime();
      }

      console.log('Closed before stop: Closing code: ', exit_code);
    });

    return child.pid;
  },
  killProcess: function(serviceID) {
    var runningService = services[serviceID];
    if (runningService) {
      console.log('kill process');
      runningService.child.kill('SIGKILL');
      runningService.status = 0;
      runningService.kill = new Date().getTime();
    }
  },
  removeProcess: function(serviceID, cb) {
    var runningService = services[serviceID];
    if (runningService) {
      this.killProcess(serviceID);
      delete services[serviceID];
    }
  },
  startDebugServer: function() {
    if (debugServerChild) {
      console.log('Debug server was already started.');
      return;
    }
    console.log('Starting debug server on port ' +
                config.NODE_INSPECTOR_WEB_PORT+ '...');

    var inspectorPath = './node_modules/node-inspector/bin/inspector.js';

    var child = new (forever.Monitor)(
      inspectorPath,
      {
        max : 100,
        silent : true,
        args: [
          '--web-port', config.NODE_INSPECTOR_WEB_PORT,
          '--save-live-edit', 'true'
        ]
      }
    );

    child.start();

    debugServerChild = child;
    child.on('restart', function() {
      console.log("Debug server was restarted automatically");
    });

    child.on('stdout', function (data) {
      console.log(child.pid, data.toString());
    });

    child.on('stderr', function (data) {
      console.log(child.pid, data.toString());
    });

    child.on('exit', function(exit_code) {
      console.log('Closed before stop: Closing code: ', exit_code);
    });

    process.on('exit', function() {
      debugServerChild.kill('SIGKILL');
    });
  },
  shutdownDebugServer: function() {
    if (debugServerChild) {
      console.log('kill debug server');
      debugServerChild.kill('SIGKILL');
      debugServerChild = null;
    }
  }
};

module.exports = ServiceManager;<|endoftext|>


<commit_before>var m = require('mithril');
var $osf = require('js/osfHelpers');
var waterbutler = require('js/waterbutler');
var AddProject = require('js/addProjectPlugin');

require('css/dropzone-plugin.css');
require('css/quick-project-search-plugin.css');
require('loaders.css/loaders.min.css');
var Dropzone = require('dropzone');
var clipboard = require("clipboard-js");
var fileURLArray = [];

// Don't show dropped content if user drags outside dropzone
window.ondragover = function (e) {
    e.preventDefault();
};
window.ondrop = function (e) {
    e.preventDefault();
};

var ShareWindowDropzone = {
    controller: function () {
        var dangerCount = 0;
        Dropzone.options.shareWindowDropzone = {
            // Dropzone is setup to upload multiple files in one request this configuration forces it to do upload file-by-
            //file, one request at a time.
            clickable: '#shareWindowDropzone',
            // number of files to process in parallel
            parallelUploads: 1,
            // prevents default uploading; call processQueue() to upload
            autoProcessQueue: false,
            withCredentials: true,
            method: 'put',
            // in MB; lower to test errors. Default is 256 MB.
            maxFilesize: 1,
            maxFiles: 1,
            // checks if file is valid; if so, then adds to queue
            init: function () {
                // When user clicks close button on top right, reset the number of files
                var _this = this;
                $('button.close').on('click', function () {
                    _this.files.length = 0;
                });

            },
            accept: function (file, done) {
                if (this.files.length <= 1) {
                    this.options.url = waterbutler.buildUploadUrl(false, 'osfstorage', window.contextVars['shareWindowId'], file, {});
                    this.processFile(file);
                }
                else {
                    dangerCount = document.getElementsByClassName('alert-danger').length;
                    if (dangerCount === 0)
                        $osf.growl("Error", "You can only upload a maximum of " + this.options.maxFiles + " files at once. " +
                            "
 To upload more files, refresh the page or click X on the top right. " +
                            "
 Want to share more files? Create a new project.", "danger", 10000);

                    return this.emit("error", file);
                }
            },

            sending: function (file, xhr) {
                //Hack to remove webkitheaders
                var _send = xhr.send;
                xhr.send = function () {
                    _send.call(xhr, file);
                };
                $('.drop-zone-format').css({'padding-bottom': '10px'});
            },

            success: function (file, xhr) {
                var fileJson = JSON.parse((file.xhr.response));
                var filePath = fileJson.path;
                var url =(window.location.host+'/'+window.contextVars['shareWindowId']+'/files/osfstorage'+ filePath);
                fileURLArray.push(url);
                this.processQueue();
                file.previewElement.classList.add("dz-success");
                file.previewElement.classList.add("dz-preview-background-success");
                if (this.getQueuedFiles().length === 0 && this.getUploadingFiles().length === 0) {
                    if (this.files.length === 1)
                        $osf.growl("Upload Successful", this.files.length + " file was successfully uploaded to your public files project.", "success", 10000);
                    else
                        $osf.growl("Upload Successful", this.files.length + " files were successfully uploaded to your public files project.", "success", 10000);

                }
            },

            error: function (file, message) {
                this.files.length--;
                // Keeping the old behavior in case we want to revert it some time
                file.previewElement.classList.add("dz-error");
                file.previewElement.classList.add("dz-preview-background-error");
                file.previewElement.remove(); // Doesn't show the preview
                // Need the padding change twice because the padding doesn't resize when there is an error
                // get file size in MB, rounded to 1 decimal place
                var fileSizeMB = Math.round(file.size / (1000 * 1000) * 10) / 10;
                if (fileSizeMB > this.options.maxFilesize) {
                    $osf.growl("Upload Failed", file.name + " could not be uploaded.
 The file is " + fileSizeMB + " MB," +
                        " which exceeds the max file size of " + this.options.maxFilesize + " MB", "danger", 5000);
                }
            },

        };

        $("#shareWindowDropzone").on("click", "div.dz-share", function (e) {
            var shareLink = "";
            var el = document.getElementsByClassName('dz-preview');
            for (var i = 0; i < el.length; i++) {
                // console.log(i +" > "+ fileURLArray[i]);
                //text(fileURLArray[i]);
                if ($(".dz-share").index(this) == i) {
                    shareLink = fileURLArray[i];
                }
            }
            clip.setData("text/plain", shareLink);
            console.log(clip.getData());

            $(e.target).parent().siblings('.alertbubble').finish().show().delay(1000).fadeOut("slow");
            $.growl({
                icon: 'fa fa-clipboard',
                message: ' Link copied to clipboard'
            }, {
                type: 'info',
                allow_dismiss: false,
                mouse_over: 'pause',
                placement: {
                    from: "top",
                    align: "center"
                }
            });
        });

        var template = m('div.dz-preview.dz-processing.dz-file-preview',
            m('div.dz-details',
                m('div.dz-filename',
                    m('span[data-dz-name]')
                ),
                m('div[data-dz-size].dz-size'),
                m('img[data-dz-thumbnail]')
            ),
            m('div.dz-progress',
                m('span[data-dz-uploadprogress].dz-upload')
            ),
            m(".dz-share", [" ", m("i.fa.fa-share-alt"), " "]), " ",

            m('div.dz-success-mark',
                m('span.glyphicon.glyphicon-ok-circle')
            ),
            m('div.dz-error-mark',
                m('span.glyphicon.glyphicon-remove-circle')
            )
        );

        $('#shareWindowDropzone').dropzone({
            url: 'placeholder',
            previewTemplate: $osf.mithrilToStr(template)
        });
        // cache the selector to avoid duplicate selector warning
        var $chevron = $('#glyphchevron');

        $('#ShareButton').click(function () {
                document.getElementById("ShareButton").style.cursor = "pointer";
                $('#shareWindowDropzone').stop().slideToggle();
                $('#shareWindowDropzone').css('display', 'inline-block');
                $chevron.toggleClass('glyphicon glyphicon-chevron-down');
                $chevron.toggleClass('glyphicon glyphicon-chevron-up');
            }
        );

    },

    view: function (ctrl, args) {
        function headerTemplate() {
            return [
                m('h2.col-xs-6', 'Dashboard'), m('m-b-lg.pull-right',
                    m('button.btn.btn-primary.m-t-md.f-w-xl #ShareButton',
                        m('span.glyphicon.glyphicon-chevron-down #glyphchevron'), ' Upload Public Files'), m.component(AddProject, {
                            buttonTemplate: m('button.btn.btn-success.btn-success-high-contrast.m-t-md.f-w-xl.pull-right[data-toggle="modal"][data-target="#addProjectFromHome"]',
                                {
                                    onclick: function () {
                                        $osf.trackClick('quickSearch', 'add-project', 'open-add-project-modal');
                                    }
                                }, 'Create new project'),
                            modalID: 'addProjectFromHome',
                            stayCallback: function _stayCallback_inPanel() {
                                document.location.reload(true);
                            },
                            trackingCategory: 'quickSearch',
                            trackingAction: 'add-project',
                            templatesFetcher: ctrl.templateNodes
                        }
                    )
                )
            ];
        }

        // Activate Public Files tooltip info
        $('[data-toggle="tooltip"]').tooltip();

        // unexplainable large margin between dashboard and quick search projects
        return m('.row', m('.col-xs-12.m-b-sm', headerTemplate()),
            m('div.p-v-xs.drop-zone-format.drop-zone-invis .pointer .panel #shareWindowDropzone',
                m('button.close[aria-label="Close"]', {
                        onclick: function () {
                            var $chevron = $('#glyphchevron');
                            $('#shareWindowDropzone').hide();
                            $('div.dz-preview').remove();
                            $chevron.toggleClass('glyphicon glyphicon-chevron-up');
                            $chevron.toggleClass('glyphicon glyphicon-chevron-down');
                            $('.drop-zone-format').css({'padding-bottom': '175px'});
                        }
                    }, m('.drop-zone-close', '×')
                ),
                m('h1.dz-p.text-center #shareWindowDropzone', 'Drop files to upload',
                    m('h5', 'Click the box to upload files. Files are automatically uploaded to your ',
                        m('a', {
                            href: '/share_window/', onclick: function (e) {
                                // Prevent clicking of link from opening file uploader
                                e.stopImmediatePropagation();
                            }
                        }, 'Public Files'), ' ', m('i.fa.fa-question-circle.text-muted', {
                            'data-toggle': 'tooltip',
                            'title': 'The Public Files Project allows you to easily collaborate and share your files with anybody.',
                            'data-placement': 'bottom'
                        }, '')
                    )
                )
            )
        );
    }
};

module.exports = ShareWindowDropzone;
<commit_msg>Remove old clip stuff<commit_after>var m = require('mithril');
var $osf = require('js/osfHelpers');
var waterbutler = require('js/waterbutler');
var AddProject = require('js/addProjectPlugin');

require('css/dropzone-plugin.css');
require('css/quick-project-search-plugin.css');
require('loaders.css/loaders.min.css');
var Dropzone = require('dropzone');
var clipboard = require("clipboard-js");
var fileURLArray = [];

// Don't show dropped content if user drags outside dropzone
window.ondragover = function (e) {
    e.preventDefault();
};
window.ondrop = function (e) {
    e.preventDefault();
};

var ShareWindowDropzone = {
    controller: function () {
        var dangerCount = 0;
        Dropzone.options.shareWindowDropzone = {
            // Dropzone is setup to upload multiple files in one request this configuration forces it to do upload file-by-
            //file, one request at a time.
            clickable: '#shareWindowDropzone',
            // number of files to process in parallel
            parallelUploads: 1,
            // prevents default uploading; call processQueue() to upload
            autoProcessQueue: false,
            withCredentials: true,
            method: 'put',
            // in MB; lower to test errors. Default is 256 MB.
            maxFilesize: 1,
            maxFiles: 1,
            // checks if file is valid; if so, then adds to queue
            init: function () {
                // When user clicks close button on top right, reset the number of files
                var _this = this;
                $('button.close').on('click', function () {
                    _this.files.length = 0;
                });

            },
            accept: function (file, done) {
                if (this.files.length <= 1) {
                    this.options.url = waterbutler.buildUploadUrl(false, 'osfstorage', window.contextVars['shareWindowId'], file, {});
                    this.processFile(file);
                }
                else {
                    dangerCount = document.getElementsByClassName('alert-danger').length;
                    if (dangerCount === 0)
                        $osf.growl("Error", "You can only upload a maximum of " + this.options.maxFiles + " files at once. " +
                            "
 To upload more files, refresh the page or click X on the top right. " +
                            "
 Want to share more files? Create a new project.", "danger", 10000);

                    return this.emit("error", file);
                }
            },

            sending: function (file, xhr) {
                //Hack to remove webkitheaders
                var _send = xhr.send;
                xhr.send = function () {
                    _send.call(xhr, file);
                };
                $('.drop-zone-format').css({'padding-bottom': '10px'});
            },

            success: function (file, xhr) {
                var fileJson = JSON.parse((file.xhr.response));
                var filePath = fileJson.path;
                var url =(window.location.host+'/'+window.contextVars['shareWindowId']+'/files/osfstorage'+ filePath);
                fileURLArray.push(url);
                this.processQueue();
                file.previewElement.classList.add("dz-success");
                file.previewElement.classList.add("dz-preview-background-success");
                if (this.getQueuedFiles().length === 0 && this.getUploadingFiles().length === 0) {
                    if (this.files.length === 1)
                        $osf.growl("Upload Successful", this.files.length + " file was successfully uploaded to your public files project.", "success", 10000);
                    else
                        $osf.growl("Upload Successful", this.files.length + " files were successfully uploaded to your public files project.", "success", 10000);

                }
            },

            error: function (file, message) {
                this.files.length--;
                // Keeping the old behavior in case we want to revert it some time
                file.previewElement.classList.add("dz-error");
                file.previewElement.classList.add("dz-preview-background-error");
                file.previewElement.remove(); // Doesn't show the preview
                // Need the padding change twice because the padding doesn't resize when there is an error
                // get file size in MB, rounded to 1 decimal place
                var fileSizeMB = Math.round(file.size / (1000 * 1000) * 10) / 10;
                if (fileSizeMB > this.options.maxFilesize) {
                    $osf.growl("Upload Failed", file.name + " could not be uploaded.
 The file is " + fileSizeMB + " MB," +
                        " which exceeds the max file size of " + this.options.maxFilesize + " MB", "danger", 5000);
                }
            },

        };

        $("#shareWindowDropzone").on("click", "div.dz-share", function (e) {
            var shareLink = "";
            var el = document.getElementsByClassName('dz-preview');
            for (var i = 0; i < el.length; i++) {
                // console.log(i +" > "+ fileURLArray[i]);
                //text(fileURLArray[i]);
                if ($(".dz-share").index(this) == i) {
                    shareLink = fileURLArray[i];
                }
            }
            $(e.target).parent().siblings('.alertbubble').finish().show().delay(1000).fadeOut("slow");
            $.growl({
                icon: 'fa fa-clipboard',
                message: ' Link copied to clipboard'
            }, {
                type: 'info',
                allow_dismiss: false,
                mouse_over: 'pause',
                placement: {
                    from: "top",
                    align: "center"
                }
            });
        });

        var template = m('div.dz-preview.dz-processing.dz-file-preview',
            m('div.dz-details',
                m('div.dz-filename',
                    m('span[data-dz-name]')
                ),
                m('div[data-dz-size].dz-size'),
                m('img[data-dz-thumbnail]')
            ),
            m('div.dz-progress',
                m('span[data-dz-uploadprogress].dz-upload')
            ),
            m(".dz-share", [" ", m("i.fa.fa-share-alt"), " "]), " ",

            m('div.dz-success-mark',
                m('span.glyphicon.glyphicon-ok-circle')
            ),
            m('div.dz-error-mark',
                m('span.glyphicon.glyphicon-remove-circle')
            )
        );

        $('#shareWindowDropzone').dropzone({
            url: 'placeholder',
            previewTemplate: $osf.mithrilToStr(template)
        });
        // cache the selector to avoid duplicate selector warning
        var $chevron = $('#glyphchevron');

        $('#ShareButton').click(function () {
                document.getElementById("ShareButton").style.cursor = "pointer";
                $('#shareWindowDropzone').stop().slideToggle();
                $('#shareWindowDropzone').css('display', 'inline-block');
                $chevron.toggleClass('glyphicon glyphicon-chevron-down');
                $chevron.toggleClass('glyphicon glyphicon-chevron-up');
            }
        );

    },

    view: function (ctrl, args) {
        function headerTemplate() {
            return [
                m('h2.col-xs-6', 'Dashboard'), m('m-b-lg.pull-right',
                    m('button.btn.btn-primary.m-t-md.f-w-xl #ShareButton',
                        m('span.glyphicon.glyphicon-chevron-down #glyphchevron'), ' Upload Public Files'), m.component(AddProject, {
                            buttonTemplate: m('button.btn.btn-success.btn-success-high-contrast.m-t-md.f-w-xl.pull-right[data-toggle="modal"][data-target="#addProjectFromHome"]',
                                {
                                    onclick: function () {
                                        $osf.trackClick('quickSearch', 'add-project', 'open-add-project-modal');
                                    }
                                }, 'Create new project'),
                            modalID: 'addProjectFromHome',
                            stayCallback: function _stayCallback_inPanel() {
                                document.location.reload(true);
                            },
                            trackingCategory: 'quickSearch',
                            trackingAction: 'add-project',
                            templatesFetcher: ctrl.templateNodes
                        }
                    )
                )
            ];
        }

        // Activate Public Files tooltip info
        $('[data-toggle="tooltip"]').tooltip();

        // unexplainable large margin between dashboard and quick search projects
        return m('.row', m('.col-xs-12.m-b-sm', headerTemplate()),
            m('div.p-v-xs.drop-zone-format.drop-zone-invis .pointer .panel #shareWindowDropzone',
                m('button.close[aria-label="Close"]', {
                        onclick: function () {
                            var $chevron = $('#glyphchevron');
                            $('#shareWindowDropzone').hide();
                            $('div.dz-preview').remove();
                            $chevron.toggleClass('glyphicon glyphicon-chevron-up');
                            $chevron.toggleClass('glyphicon glyphicon-chevron-down');
                            $('.drop-zone-format').css({'padding-bottom': '175px'});
                        }
                    }, m('.drop-zone-close', '×')
                ),
                m('h1.dz-p.text-center #shareWindowDropzone', 'Drop files to upload',
                    m('h5', 'Click the box to upload files. Files are automatically uploaded to your ',
                        m('a', {
                            href: '/share_window/', onclick: function (e) {
                                // Prevent clicking of link from opening file uploader
                                e.stopImmediatePropagation();
                            }
                        }, 'Public Files'), ' ', m('i.fa.fa-question-circle.text-muted', {
                            'data-toggle': 'tooltip',
                            'title': 'The Public Files Project allows you to easily collaborate and share your files with anybody.',
                            'data-placement': 'bottom'
                        }, '')
                    )
                )
            )
        );
    }
};

module.exports = ShareWindowDropzone;
<|endoftext|>


<commit_before>'use strict'

let sql = require('sql')
let Query = require('./query')

class Model {

  constructor (data) {
    this.constructor.defineProperties()
    this.data = new Map()
    for (let key in data) if (this.properties[key]) this[key] = data[key]
  }

  get db () {
    return this.constructor.db
  }

  get table () {
    return this.constructor.table
  }

  get tableName () {
    return this.constructor.tableName
  }

  get colunns () {
    return this.constructor.columns
  }

  get relations () {
    return this.constructor.relations
  }

  get properties () {
    return this.constructor.properties
  }

  slice () {
    let result = {}
    for (let key of arguments) result[key] = this[key]
    return result
  }

  update (values) {
    for (let key in values) this[key] = values[key]
    return new Query(this.constructor)
    .where({id: this.id})
    .update(this.slice.apply(this, Object.keys(values)))
  }

  destroy () {
    return new Query(this.constructor).where({id: this.id}).delete()
  }

  static get table () {
    if (!this._table) {
      this._table = sql.define({name: this.tableName, columns: this.columns})
    }
    return this._table
  }

  static get properties () {
    if (!this._properties) {
      this._properties = {}
      for (let column of this.table.columns) {
        this._properties[column.property] = column
      }
    }
    return this._properties
  }

  static get relations () {
    if (!this._relations) this._relations = {}
    return this._relations
  }

  static create (values) {
    return new Query(this).insert(values)
  }

  static hasMany (name, options) {
    options.many = true
    this.relations[name] = options
  }

  static belongsTo (name, options) {
    options.many = false
    this.relations[name] = options
  }

  static defineProperties () {
    if (this._propsDefined) return
    this._propsDefined = true
    for (let column of this.table.columns) {
      if (Object.getOwnPropertyDescriptor(this.prototype, column.property)) {
        continue
      }
      Object.defineProperty(this.prototype, column.property, {
        get: function () { return this.data.get(column.name) },
        set: function (value) { this.data.set(column.name, value) }
      })
    }
  }

}

// Attach Query methods to Model
let queryMethods = [
  'all',
  'find',
  'include',
  'limit',
  'match',
  'offset',
  'order',
  'where'
]

for (let method of queryMethods) {
  Model[method] = function () {
    let query = new Query(this)
    return query[method].apply(query, arguments)
  }
}

module.exports = Model
<commit_msg>Swap property names.<commit_after>'use strict'

let sql = require('sql')
let Query = require('./query')

class Model {

  constructor (data) {
    this.constructor.defineProperties()
    this.data = new Map()
    for (let key in data) if (this.properties[key]) this[key] = data[key]
  }

  get db () {
    return this.constructor.db
  }

  get table () {
    return this.constructor.table
  }

  get tableName () {
    return this.constructor.tableName
  }

  get colunns () {
    return this.constructor.columns
  }

  get relations () {
    return this.constructor.relations
  }

  get properties () {
    return this.constructor.properties
  }

  slice () {
    let result = {}
    for (let key of arguments) result[key] = this[key]
    return result
  }

  update (values) {
    for (let key in values) this[key] = values[key]
    return new Query(this.constructor)
    .where({id: this.id})
    .update(this.slice.apply(this, Object.keys(values)))
  }

  destroy () {
    return new Query(this.constructor).where({id: this.id}).delete()
  }

  static get table () {
    if (!this._table) {
      this._table = sql.define({name: this.tableName, columns: this.columns})
    }
    return this._table
  }

  static get properties () {
    if (!this._properties) {
      this._properties = {}
      for (let column of this.table.columns) {
        this._properties[column.property] = column
      }
    }
    return this._properties
  }

  static get relations () {
    if (!this._relations) this._relations = {}
    return this._relations
  }

  static create (values) {
    return new Query(this).insert(values)
  }

  static hasMany (name, options) {
    options.many = true
    this.relations[name] = options
  }

  static belongsTo (name, options) {
    options.many = false
    this.relations[name] = options
  }

  static defineProperties () {
    if (this._propsDefined) return
    this._propsDefined = true
    for (let column of this.table.columns) {
      if (Object.getOwnPropertyDescriptor(this.prototype, column.property)) {
        continue
      }
      Object.defineProperty(this.prototype, column.property, {
        get: function () {
          return this.data.get(column.property)
        },
        set: function (value) {
          this.data.set(column.property, value)
        }
      })
    }
  }

}

// Attach Query methods to Model
let queryMethods = [
  'all',
  'find',
  'include',
  'limit',
  'match',
  'offset',
  'order',
  'where'
]

for (let method of queryMethods) {
  Model[method] = function () {
    let query = new Query(this)
    return query[method].apply(query, arguments)
  }
}

module.exports = Model
<|endoftext|>


<commit_before>var async      = require('asyncjs'),
    path       = require('path'),
    utils      = require('../utils'),
    errors     = require('../errors'),
    blacklist  = require('../blacklist'),
    Observable = utils.Observable;

module.exports = Observable.extend({
  constructor: function BinHandler() {
    Observable.apply(this, arguments);

    // For now we bind all methods to the class scope. In reality only those
    // used as route callbacks need to be bound.
    var methods = Object.getOwnPropertyNames(BinHandler.prototype).filter(function (prop) {
      return typeof this[prop] === 'function';
    }, this);

    utils.bindAll(this, methods);
  },
  getDefault: function (req, res) {
    this.renderFiles(req, res);
  },
  getBin: function (req, res, next) {
    this.render(req, res, req.bin);
  },
  getBinPreview: function (req, res, next) {
    var options = {edit: !req.param('quiet')};
    this.formatPreview(req.bin, req.helpers, options, function (err, formatted) {
      if (err) {
        next(err);
      }

      if (formatted) {
        res.send(formatted);
      } else {
        res.contentType('js');
        res.send(req.bin.javascript);
      }
    });
  },
  getBinSource: function (req, res) {
    res.contentType('json');
    var output = JSON.stringify(this.templateFromBin(req.bin));
    if (!req.ajax) {
      res.contentType('js');
      output = 'var template = ' + output;
    }
    res.send(output);
  },
  getBinSourceFile: function (req, res) {
    var format = req.params.format;

    res.contentType(format);
    if (format !== 'html') {
      if (format === 'js' || format === 'json') {
        format = 'javascript';
      }
      res.send(req.bin[format]);
    } else {
      this.getBinPreview(req, res);
    }
  },
  getUserBins: function (req, res, next) {
    if (!req.session.user) {
      return res.send('');
    }

    req.models.user.getBins(req.session.user.name, function (err, bins) {
      if (err) {
        return next(err);
      }
      this.renderHistory(req, res, bins);
    });
  },
  redirectToLatest: function (req, res) {
    var path = req.originalUrl.replace('latest', req.bin.revision);
    res.redirect(303, path);
  },
  createBin: function (req, res, next) {
    var params = utils.extract(req.body, 'html', 'css', 'javascript'),
        _this  = this;

    this.validateBin(params, function (err) {
      if (err) {
        return next(err);
      }

      req.models.bin.create(params, function (err, result) {
        if (err) {
          return next(err);
        }

        _this.completeCreateBin(result, req, res, next);
      });
    });
  },
  createRevision: function (req, res, next) {
    var panel  = req.param('panel'),
        params = {},
        _this  = this;

    if (req.param('method') === 'save') {
      params = utils.extract(req.body, 'html', 'css', 'javascript');
      params.url = req.bin.url;
      params.revision = req.bin.revision + 1;

      this.validateBin(params, function (err) {
        if (err) {
          return next(err);
        }

        req.models.bin.createRevision(params, function (err, result) {
          if (err) {
            return next(err);
          }

          _this.completeCreateBin(result, req, res, next);
        });
      });
    } else if (req.param('method') === 'update') {
      params[panel] = req.param('content');
      params.streamingKey = req.param('checksum');
      params.revision = req.param('revision');
      params.url = req.param('code');

      this.validateBin(params, function (err) {
        if (err) {
          return next(err);
        }

        req.models.bin.updatePanel(panel, params, function (err, result) {
          if (err) {
            return next(err);
          }
          res.json({ok: true, error: false});
        });
      });
    } else {
      next();
    }
  },
  completeCreateBin: function (bin, req, res, next) {
    var render = this.renderCreated.bind(this, req, res, bin);

    // If we have a logged in user then assign the bin to them.
    if (req.session.user && req.session.user.name) {
      req.models.user.assignBin(req.session.user.name, bin, function (err) {
        if (err) {
          return next(err);
        }
        render();
      });
    } else {
      render();
    }
  },
  downloadBin: function (req, res, next) {
    var bin = req.bin,
        filename = ['jsbin', bin.url, bin.revision, 'html'].join('.'),
        options  = {analytics: false, edit: false};

    this.formatPreview(bin, req.helpers, options, function (err, formatted) {
      if (err) {
        next(err);
      }

      res.header('Content-Disposition', 'attachment; filename=' + filename);

      if (formatted) {
        res.send(formatted);
      } else {
        res.contentType('js');
        res.send(bin.javascript);
      }
    });
  },
  notFound: function (req, res) {
    var files = this.defaultFiles();
    files[0] = 'not_found.html';
    files[2] = 'not_found.js';
    this.renderFiles(req, res, files);
  },
  loadBin: function (req, res, next) {
    var rev    = parseInt(req.params.rev, 10) || 1,
        query  = {id: req.params.bin, revision: rev};

    function complete(err, result) {
      if (err) {
        return next(new errors.NotFound('Could not find bin: ' + req.params.bin));
      } else {
        req.bin = result;
        next();
      }
    }

    // TODO: Re-factor this logic.
    if ((req.params.rev || req.path.indexOf('latest') === -1) && req.path.indexOf('save') === -1) {
      req.models.bin.load(query, complete);
    } else {
      req.models.bin.latest(query, complete);
    }
  },
  validateBin: function (bin, fn) {
    if (!blacklist.validate(bin)) {
      fn(new errors.BadRequest('Unable to save: Post contains blacklisted content'));
    } else {
      fn();
    }
  },
  render: function (req, res, bin) {
    var template = this.templateFromBin(bin),
        jsbin = this.jsbin(bin, req.helpers.production ? req.helpers.set('version') : 'debug', req.session._csrf),
        _this = this;

    function onComplete(err, history) {
      req.helpers.analytics(function (err, analytics) {
        res.render('index', {
          tips: '{}',
          revision: bin.revision || 1,
          home: req.session.user ? req.session.user.name : null,
          jsbin: JSON.stringify(jsbin),
          json_template: JSON.stringify(template),
          version: jsbin.version,
          analytics: analytics,
          token: req.session._csrf,
          url: req.path,
          list_history: history || '',
          'production?': req.helpers.production
        });
      });
    }

    if (req.session.user) {
      req.models.user.getBins(req.session.user.name, function (err, bins) {
        if (err) {
          return onComplete(err);
        }
        _this.formatHistory(bins, req.helpers, onComplete);
      });
    } else {
      onComplete();
    }
  },
  renderFiles: function (req, res, files) {
    var _this = this;

    files = files || this.defaultFiles();
    this.loadFiles(files, req.helpers, function (err, results) {
      if (!err) {
        _this.render(req, res, results);
      }
    });
  },
  renderCreated: function (req, res, bin) {
    var permalink = req.helpers.urlForBin(bin),
        editPermalink = req.helpers.editUrlForBin(bin);

    if (req.ajax) {
      if (req.param('format', '').toLowerCase() === 'plain') {
        return res.contentType('txt').send(req.helpers.editUrlForBin(bin, true));
      }

      res.json({
        code: bin.url,
        root: req.helpers.set('url full'),
        created: (new Date()).toISOString(), // Should be part of bin.
        revision: bin.revision,
        url: permalink,
        edit: editPermalink,
        html: editPermalink,
        js: editPermalink,
        title: utils.titleForBin(bin),
        allowUpdate: !!bin.streamingKey,
        checksum: bin.streamingKey
      });
    } else {
      res.redirect(303, req.helpers.url(bin.url + '/' + bin.revision + '/edit'));
    }
  },
  renderHistory: function (req, res, bins) {
    this.formatHistory(bins, req.helpers, function (err, history) {
      res.send(history);
    });
  },
  jsbin: function (bin, version, token) {
    return {
      root: '',
      version: version,
      state: {
        token: token,
        stream: false,
        code: bin.url || null,
        revision: bin.revision || 1
      }
    };
  },
  templateFromBin: function (bin) {
    return utils.extract(bin, 'html', 'css', 'javascript');
  },
  defaultFiles: function () {
    return ['html', 'css', 'js'].map(function (ext) {
      return 'default.' + ext;
    });
  },
  loadFiles: function (files, helpers, fn) {
    files = files || this.defaultFiles();
    async.files(files, helpers.set('views')).readFile("utf8").toArray(function (err, results) {
      if (!err) {
        fn(null, {
          html: results[0].data,
          css: results[1].data,
          javascript: results[2].data
        });
      } else {
        fn(err);
      }
    });
  },
  formatPreview: function (bin, helpers, options, fn) {
    var formatted = bin.html || '',
        insert = [], parts, last, context;

    function onAnalyticsComplete(err, analytics) {
      if (err) {
        return fn(err);
      }

      if (formatted.indexOf('%code%') > -1) {
        formatted = formatted.replace(/%code%/g, bin.javascript);
      } else {
        insert.push('');
      }

      if (!options || options.edit !== false) {
        insert.push('');
      }

      if (helpers.production && analytics) {
        insert.push(analytics);
      }

      // Append scripts to the bottom of the page.
      if (insert.length) {
        parts = formatted.split('');
        last  = parts.pop();
        formatted = parts.join('') + insert.join('\n') + '\n' + last;
      }

      if (formatted.indexOf('%css%') > -1) {
        formatted = formatted.replace(/%css%/g, bin.css || '');
      } else {
        insert = '';
        parts = formatted.split('');
        last  = parts.pop();
        formatted = parts.join('') + insert + '' + last;
      }

      context = {
        domain: helpers.set('url host'),
        permalink: helpers.editUrlForBin(bin, true)
      };

      // Append attribution comment to header.
      helpers.render('comment', context, function (err, comment) {
        formatted = formatted.replace(/]*>/, function ($0) {
          return $0 + '\n' + comment.trim();
        });
        return fn(err || null, err ? undefined : formatted);
      });
    }

    // TODO: Re implement this entire block with an HTML parser.
    if (formatted) {
      if (options.analytics !== false) {
        helpers.analytics(onAnalyticsComplete);
      } else {
        onAnalyticsComplete();
      }
    } else {
      fn(null, formatted);
    }
  },
  formatHistory: function (bins, helpers, fn) {
    this.loadFiles(null, helpers, function (err, defaults) {
      var map = {}, data = [], key;

      bins.forEach(function (bin) {
        var query = utils.queryStringForBin(bin, defaults),
            revisions = map[bin.url];

        if (!revisions) {
          revisions = map[bin.url] = [];
          data.push(revisions);
        }

        revisions.push({
          title: utils.titleForBin(bin),
          code: bin.url,
          revision: bin.revision,
          url: helpers.urlForBin(bin),
          edit_url: helpers.editUrlForBin(bin) + '?' + query,
          created: bin.created.toISOString(),
          pretty_created: utils.since(bin.created),
          is_first: !map[bin.url].length
        });
      });

      helpers.render('history', {bins: data}, fn);
    });
  }
});
<commit_msg>Include the page root in the jsbin object<commit_after>var async      = require('asyncjs'),
    path       = require('path'),
    utils      = require('../utils'),
    errors     = require('../errors'),
    blacklist  = require('../blacklist'),
    Observable = utils.Observable;

module.exports = Observable.extend({
  constructor: function BinHandler() {
    Observable.apply(this, arguments);

    // For now we bind all methods to the class scope. In reality only those
    // used as route callbacks need to be bound.
    var methods = Object.getOwnPropertyNames(BinHandler.prototype).filter(function (prop) {
      return typeof this[prop] === 'function';
    }, this);

    utils.bindAll(this, methods);
  },
  getDefault: function (req, res) {
    this.renderFiles(req, res);
  },
  getBin: function (req, res, next) {
    this.render(req, res, req.bin);
  },
  getBinPreview: function (req, res, next) {
    var options = {edit: !req.param('quiet')};
    this.formatPreview(req.bin, req.helpers, options, function (err, formatted) {
      if (err) {
        next(err);
      }

      if (formatted) {
        res.send(formatted);
      } else {
        res.contentType('js');
        res.send(req.bin.javascript);
      }
    });
  },
  getBinSource: function (req, res) {
    res.contentType('json');
    var output = JSON.stringify(this.templateFromBin(req.bin));
    if (!req.ajax) {
      res.contentType('js');
      output = 'var template = ' + output;
    }
    res.send(output);
  },
  getBinSourceFile: function (req, res) {
    var format = req.params.format;

    res.contentType(format);
    if (format !== 'html') {
      if (format === 'js' || format === 'json') {
        format = 'javascript';
      }
      res.send(req.bin[format]);
    } else {
      this.getBinPreview(req, res);
    }
  },
  getUserBins: function (req, res, next) {
    if (!req.session.user) {
      return res.send('');
    }

    req.models.user.getBins(req.session.user.name, function (err, bins) {
      if (err) {
        return next(err);
      }
      this.renderHistory(req, res, bins);
    });
  },
  redirectToLatest: function (req, res) {
    var path = req.originalUrl.replace('latest', req.bin.revision);
    res.redirect(303, path);
  },
  createBin: function (req, res, next) {
    var params = utils.extract(req.body, 'html', 'css', 'javascript'),
        _this  = this;

    this.validateBin(params, function (err) {
      if (err) {
        return next(err);
      }

      req.models.bin.create(params, function (err, result) {
        if (err) {
          return next(err);
        }

        _this.completeCreateBin(result, req, res, next);
      });
    });
  },
  createRevision: function (req, res, next) {
    var panel  = req.param('panel'),
        params = {},
        _this  = this;

    if (req.param('method') === 'save') {
      params = utils.extract(req.body, 'html', 'css', 'javascript');
      params.url = req.bin.url;
      params.revision = req.bin.revision + 1;

      this.validateBin(params, function (err) {
        if (err) {
          return next(err);
        }

        req.models.bin.createRevision(params, function (err, result) {
          if (err) {
            return next(err);
          }

          _this.completeCreateBin(result, req, res, next);
        });
      });
    } else if (req.param('method') === 'update') {
      params[panel] = req.param('content');
      params.streamingKey = req.param('checksum');
      params.revision = req.param('revision');
      params.url = req.param('code');

      this.validateBin(params, function (err) {
        if (err) {
          return next(err);
        }

        req.models.bin.updatePanel(panel, params, function (err, result) {
          if (err) {
            return next(err);
          }
          res.json({ok: true, error: false});
        });
      });
    } else {
      next();
    }
  },
  completeCreateBin: function (bin, req, res, next) {
    var render = this.renderCreated.bind(this, req, res, bin);

    // If we have a logged in user then assign the bin to them.
    if (req.session.user && req.session.user.name) {
      req.models.user.assignBin(req.session.user.name, bin, function (err) {
        if (err) {
          return next(err);
        }
        render();
      });
    } else {
      render();
    }
  },
  downloadBin: function (req, res, next) {
    var bin = req.bin,
        filename = ['jsbin', bin.url, bin.revision, 'html'].join('.'),
        options  = {analytics: false, edit: false};

    this.formatPreview(bin, req.helpers, options, function (err, formatted) {
      if (err) {
        next(err);
      }

      res.header('Content-Disposition', 'attachment; filename=' + filename);

      if (formatted) {
        res.send(formatted);
      } else {
        res.contentType('js');
        res.send(bin.javascript);
      }
    });
  },
  notFound: function (req, res) {
    var files = this.defaultFiles();
    files[0] = 'not_found.html';
    files[2] = 'not_found.js';
    this.renderFiles(req, res, files);
  },
  loadBin: function (req, res, next) {
    var rev    = parseInt(req.params.rev, 10) || 1,
        query  = {id: req.params.bin, revision: rev};

    function complete(err, result) {
      if (err) {
        return next(new errors.NotFound('Could not find bin: ' + req.params.bin));
      } else {
        req.bin = result;
        next();
      }
    }

    // TODO: Re-factor this logic.
    if ((req.params.rev || req.path.indexOf('latest') === -1) && req.path.indexOf('save') === -1) {
      req.models.bin.load(query, complete);
    } else {
      req.models.bin.latest(query, complete);
    }
  },
  validateBin: function (bin, fn) {
    if (!blacklist.validate(bin)) {
      fn(new errors.BadRequest('Unable to save: Post contains blacklisted content'));
    } else {
      fn();
    }
  },
  render: function (req, res, bin) {
    var template = this.templateFromBin(bin),
        helpers = req.helpers,
        version = helpers.production ? helpers.set('version') : 'debug',
        jsbin = this.jsbin(bin, version, req.session._csrf, helpers.set('url prefix')),
        _this = this;

    function onComplete(err, history) {
      req.helpers.analytics(function (err, analytics) {
        res.render('index', {
          tips: '{}',
          revision: bin.revision || 1,
          home: req.session.user ? req.session.user.name : null,
          jsbin: JSON.stringify(jsbin),
          json_template: JSON.stringify(template),
          version: jsbin.version,
          analytics: analytics,
          token: req.session._csrf,
          url: req.path,
          list_history: history || '',
          'production?': req.helpers.production
        });
      });
    }

    if (req.session.user) {
      req.models.user.getBins(req.session.user.name, function (err, bins) {
        if (err) {
          return onComplete(err);
        }
        _this.formatHistory(bins, req.helpers, onComplete);
      });
    } else {
      onComplete();
    }
  },
  renderFiles: function (req, res, files) {
    var _this = this;

    files = files || this.defaultFiles();
    this.loadFiles(files, req.helpers, function (err, results) {
      if (!err) {
        _this.render(req, res, results);
      }
    });
  },
  renderCreated: function (req, res, bin) {
    var permalink = req.helpers.urlForBin(bin),
        editPermalink = req.helpers.editUrlForBin(bin);

    if (req.ajax) {
      if (req.param('format', '').toLowerCase() === 'plain') {
        return res.contentType('txt').send(req.helpers.editUrlForBin(bin, true));
      }

      res.json({
        code: bin.url,
        root: req.helpers.set('url full'),
        created: (new Date()).toISOString(), // Should be part of bin.
        revision: bin.revision,
        url: permalink,
        edit: editPermalink,
        html: editPermalink,
        js: editPermalink,
        title: utils.titleForBin(bin),
        allowUpdate: !!bin.streamingKey,
        checksum: bin.streamingKey
      });
    } else {
      res.redirect(303, req.helpers.url(bin.url + '/' + bin.revision + '/edit'));
    }
  },
  renderHistory: function (req, res, bins) {
    this.formatHistory(bins, req.helpers, function (err, history) {
      res.send(history);
    });
  },
  jsbin: function (bin, version, token, root) {
    return {
      root: root,
      version: version,
      state: {
        token: token,
        stream: false,
        code: bin.url || null,
        revision: bin.revision || 1
      }
    };
  },
  templateFromBin: function (bin) {
    return utils.extract(bin, 'html', 'css', 'javascript');
  },
  defaultFiles: function () {
    return ['html', 'css', 'js'].map(function (ext) {
      return 'default.' + ext;
    });
  },
  loadFiles: function (files, helpers, fn) {
    files = files || this.defaultFiles();
    async.files(files, helpers.set('views')).readFile("utf8").toArray(function (err, results) {
      if (!err) {
        fn(null, {
          html: results[0].data,
          css: results[1].data,
          javascript: results[2].data
        });
      } else {
        fn(err);
      }
    });
  },
  formatPreview: function (bin, helpers, options, fn) {
    var formatted = bin.html || '',
        insert = [], parts, last, context;

    function onAnalyticsComplete(err, analytics) {
      if (err) {
        return fn(err);
      }

      if (formatted.indexOf('%code%') > -1) {
        formatted = formatted.replace(/%code%/g, bin.javascript);
      } else {
        insert.push('');
      }

      if (!options || options.edit !== false) {
        insert.push('');
      }

      if (helpers.production && analytics) {
        insert.push(analytics);
      }

      // Append scripts to the bottom of the page.
      if (insert.length) {
        parts = formatted.split('');
        last  = parts.pop();
        formatted = parts.join('') + insert.join('\n') + '\n' + last;
      }

      if (formatted.indexOf('%css%') > -1) {
        formatted = formatted.replace(/%css%/g, bin.css || '');
      } else {
        insert = '';
        parts = formatted.split('');
        last  = parts.pop();
        formatted = parts.join('') + insert + '' + last;
      }

      context = {
        domain: helpers.set('url host'),
        permalink: helpers.editUrlForBin(bin, true)
      };

      // Append attribution comment to header.
      helpers.render('comment', context, function (err, comment) {
        formatted = formatted.replace(/]*>/, function ($0) {
          return $0 + '\n' + comment.trim();
        });
        return fn(err || null, err ? undefined : formatted);
      });
    }

    // TODO: Re implement this entire block with an HTML parser.
    if (formatted) {
      if (options.analytics !== false) {
        helpers.analytics(onAnalyticsComplete);
      } else {
        onAnalyticsComplete();
      }
    } else {
      fn(null, formatted);
    }
  },
  formatHistory: function (bins, helpers, fn) {
    this.loadFiles(null, helpers, function (err, defaults) {
      var map = {}, data = [], key;

      bins.forEach(function (bin) {
        var query = utils.queryStringForBin(bin, defaults),
            revisions = map[bin.url];

        if (!revisions) {
          revisions = map[bin.url] = [];
          data.push(revisions);
        }

        revisions.push({
          title: utils.titleForBin(bin),
          code: bin.url,
          revision: bin.revision,
          url: helpers.urlForBin(bin),
          edit_url: helpers.editUrlForBin(bin) + '?' + query,
          created: bin.created.toISOString(),
          pretty_created: utils.since(bin.created),
          is_first: !map[bin.url].length
        });
      });

      helpers.render('history', {bins: data}, fn);
    });
  }
});
<|endoftext|>