<commit_before>/*
 * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
 * under one or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership. Camunda licenses this file to you under the Apache License,
 * Version 2.0; you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var addApiAttributes = require('./getApiAttributes');
var getPassthroughData = require('./getPassthroughData');
var loadPlugins = require('./loadPlugins');

module.exports = async function(config, module, appName) {
  const plugins = await loadPlugins(config, appName);

  plugins.forEach(plugin => {
    const pluginDirectiveUID = Math.random()
      .toString(36)
      .substring(2);

    // overlay function for diagram overlay plugins
    plugin.overlay = [
      'control',
      '$scope',
      ({getViewer}, scope) => {
        plugin.render(
          getViewer(),
          addApiAttributes(
            getPassthroughData(plugin.pluginPoint, scope, appName),
            config.csrfCookieName
          ),
          scope // The 'scope' argument is deprecated and should not be used - it will be removed in future releases
        );

        scope.$on('$destroy', () => {
          plugin.unmount && plugin.unmount();
        });
      }
    ];

    module.directive('pluginBridge' + pluginDirectiveUID, [
      function() {
        return {
          link: function(scope, element) {
            const isolatedContainer = document.createElement('div');
            plugin.render(
              isolatedContainer,
              addApiAttributes(
                getPassthroughData(plugin.pluginPoint, scope, appName),
                config.csrfCookieName,
                appName
              ),
              scope // The 'scope' argument is deprecated and should not be used - it will be removed in future releases
            );

            element[0].appendChild(isolatedContainer);
            scope.$on('$destroy', () => {
              plugin.unmount && plugin.unmount();
            });
          }
        };
      }
    ]);

    module.config([
      'ViewsProvider',
      function(ViewsProvider) {
        ViewsProvider.registerDefaultView(plugin.pluginPoint, {
          ...plugin.properties, // For backwards-compatibility with 'label' property
          ...plugin,
          template: `<div plugin-bridge${pluginDirectiveUID} />`
        });
      }
    ]);

    if (plugin.pluginPoint === `${appName}.route`) {
      module.config([
        '$routeProvider',
        function($routeProvider) {
          $routeProvider.when(plugin.properties.path, {
            template: `<div plugin-bridge${pluginDirectiveUID} />`,
            authentication: 'required'
          });
        }
      ]);
    }
  });
};
<commit_msg>fix(plugins): hide breadcrumbs on custom route<commit_after>/*
 * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
 * under one or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership. Camunda licenses this file to you under the Apache License,
 * Version 2.0; you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var addApiAttributes = require('./getApiAttributes');
var getPassthroughData = require('./getPassthroughData');
var loadPlugins = require('./loadPlugins');

module.exports = async function(config, module, appName) {
  const plugins = await loadPlugins(config, appName);

  plugins.forEach(plugin => {
    const pluginDirectiveUID = Math.random()
      .toString(36)
      .substring(2);

    // overlay function for diagram overlay plugins
    plugin.overlay = [
      'control',
      '$scope',
      ({getViewer}, scope) => {
        plugin.render(
          getViewer(),
          addApiAttributes(
            getPassthroughData(plugin.pluginPoint, scope, appName),
            config.csrfCookieName
          ),
          scope // The 'scope' argument is deprecated and should not be used - it will be removed in future releases
        );

        scope.$on('$destroy', () => {
          plugin.unmount && plugin.unmount();
        });
      }
    ];

    module.directive('pluginBridge' + pluginDirectiveUID, [
      function() {
        return {
          link: function(scope, element) {
            const isolatedContainer = document.createElement('div');
            plugin.render(
              isolatedContainer,
              addApiAttributes(
                getPassthroughData(plugin.pluginPoint, scope, appName),
                config.csrfCookieName,
                appName
              ),
              scope // The 'scope' argument is deprecated and should not be used - it will be removed in future releases
            );

            element[0].appendChild(isolatedContainer);
            scope.$on('$destroy', () => {
              plugin.unmount && plugin.unmount();
            });
          }
        };
      }
    ]);

    module.config([
      'ViewsProvider',
      function(ViewsProvider) {
        ViewsProvider.registerDefaultView(plugin.pluginPoint, {
          ...plugin.properties, // For backwards-compatibility with 'label' property
          ...plugin,
          template: `<div plugin-bridge${pluginDirectiveUID} />`
        });
      }
    ]);

    if (plugin.pluginPoint === `${appName}.route`) {
      module.config([
        '$routeProvider',
        function($routeProvider) {
          $routeProvider.when(plugin.properties.path, {
            template: `<div plugin-bridge${pluginDirectiveUID} />`,
            controller: [
              '$scope',
              function($scope) {
                $scope.$root.showBreadcrumbs = false;
              }
            ],
            authentication: 'required'
          });
        }
      ]);
    }
  });
};
<|endoftext|>


<commit_before>"use strict";

var Entity = require("./entity");

function AnimatedEntity(x, y, width, height, sprite, spriteOffsetX, spriteOffsetY) {
	this.sprite = sprite;
	this.spriteOffsetX = spriteOffsetX;
	this.spriteOffsetY = spriteOffsetY;
	Entity.call(this, x, y, width, height);
}
AnimatedEntity.prototype = Object.create(Entity.prototype);
AnimatedEntity.prototype.move = function(elapsedMillis) {
	Entity.prototype.move.call(this, elapsedMillis);
	if (typeof this.sprite.move === "function") {
		this.sprite.move(elapsedMillis);
	}
};
AnimatedEntity.prototype.draw = function(context) {
	if (typeof this.sprite.draw === "function") {
		this.sprite.draw(context, this.x + this.spriteOffsetX, this.y + this.spriteOffsetY);
	} else {
		context.drawImage(this.sprite, this.x + this.spriteOffsetX, this.y + this.spriteOffsetY);
	}
	// draw bounding boxes
	// context.strokeStyle = "#ff0000";
	// context.strokeRect(this.x, this.y, this.width, this.height);
};
AnimatedEntity.prototype.copy = function() {
	return new AnimatedEntity(this.x, this.y, this.width, this.height, this.sprite, this.spriteOffsetX, this.spriteOffsetY);
};

module.exports = AnimatedEntity;
<commit_msg>Add AnimatedEntity docs.<commit_after>"use strict";

var Entity = require("./entity");

/**
 * An upgraded {@link Entity} that knows how to draw and advance an {@link Animation}.
 * @constructor
 * @param {number} x The top-left x coordinate. See {@link Entity#x}
 * @param {number} y The top-left y coordinate. See {@link Entity#y}
 * @param {number} width The width on the x-axis. See {@link Entity#width}
 * @param {number} height The height on the y-axis. See {@link Entity#height}
 * @param {Animation|external:image} sprite The Animation or image to draw
 * @param {number} spriteOffsetX How much to offset {@link AnimatedEntity#sprite} from the {@link Entity#x} when drawing
 * @param {number} spriteOffsetY How much to offset {@link AnimatedEntity#sprite} from the {@link Entity#y} when drawing
 */
function AnimatedEntity(x, y, width, height, sprite, spriteOffsetX, spriteOffsetY) {
	/**
	 * The Animation or image to draw
	 * @member {Animation|external:image}
	 */
	this.sprite = sprite;
	/**
	 * How much to offset {@link AnimatedEntity#sprite} from {@link Entity#x} when drawing.
	 * @member {number}
	 */
	this.spriteOffsetX = spriteOffsetX;
	/**
	 * How much to offset {@link AnimatedEntity#sprite} from {@link Entity#y} when drawing.
	 * @member {number}
	 */
	this.spriteOffsetY = spriteOffsetY;
	Entity.call(this, x, y, width, height);
}
AnimatedEntity.prototype = Object.create(Entity.prototype);
/**
 * Simulate movement since the last frame, changing {@link Entity#x}, {@link Entity#y}, and calling {@link Animation#move} if applicable.
 * @param {number} elapsedMillis The number of milliseconds since the last frame.
 */
AnimatedEntity.prototype.move = function(elapsedMillis) {
	Entity.prototype.move.call(this, elapsedMillis);
	if (typeof this.sprite.move === "function") {
		this.sprite.move(elapsedMillis);
	}
};
/**
 * Draw the {@link AnimatedEntity#sprite}.
 * @param {external:CanvasRenderingContext2D} context The drawing context.
 */
AnimatedEntity.prototype.draw = function(context) {
	if (typeof this.sprite.draw === "function") {
		this.sprite.draw(context, this.x + this.spriteOffsetX, this.y + this.spriteOffsetY);
	} else {
		context.drawImage(this.sprite, this.x + this.spriteOffsetX, this.y + this.spriteOffsetY);
	}
	// draw bounding boxes
	// context.strokeStyle = "#ff0000";
	// context.strokeRect(this.x, this.y, this.width, this.height);
};
/**
 * Make a copy of this AnimatedEntity. The {@link AnimatedEntity#sprite} is not copied, so both entities will share it.
 * @returns {AnimatedEntity}
 */
AnimatedEntity.prototype.copy = function() {
	return new AnimatedEntity(this.x, this.y, this.width, this.height, this.sprite, this.spriteOffsetX, this.spriteOffsetY);
};

module.exports = AnimatedEntity;
<|endoftext|>


<commit_before>var common = require('./common'),
	config = require('./config').config,
	exec = require('child_process').exec,
	formidable = require('../formidable'),
	fs = require('fs'),
	io = require('../socket.io'),
	jsontemplate = require('./json-template'),
	http = require('http'),
	path = require('path'),
	tripcode = require('./tripcode');

var threads = [];
var posts = {};
var post_counter = 1;
var clients = {};
var dispatcher = {};

var sync_number = 0;
var backlog = [];
var backlog_last_dropped = 0;
var BL_WHEN = 0, BL_MSG = 1, BL_THREAD = 2;

function multisend(client, msgs) {
	client.socket.send(JSON.stringify(msgs));
}

function broadcast(msg, post, origin) {
	var thread_num = post.op || post.num;
	++sync_number;
	msg = JSON.stringify(msg);
	var payload = '[' + msg + ']';
	for (id in clients) {
		var client = clients[id];
		if (client.watching && client.watching != thread_num)
			multisend(client, [[common.INVALID]]);
		else if (id == origin) {
			/* Client won't increment SYNC since they won't
			 * receive the broadcasted message, so do manually */
			multisend(client, [[common.SYNCHRONIZE, sync_number]]);
		}
		else if (client.synced)
			client.socket.send(payload);
	}
	var now = new Date().getTime();
	backlog.push([now, msg, thread_num]);
	cleanup_backlog(now);
}

function cleanup_backlog(now) {
	var limit = now - config.BACKLOG_PERIOD;
	/* binary search would be nice */
	while (backlog.length && backlog[0][BL_WHEN] < limit) {
		backlog.shift();
		backlog_last_dropped++;
	}
}

dispatcher[common.SYNCHRONIZE] = function (msg, client) {
	if (msg.length != 2)
		return false;
	var sync = msg[0], watching = msg[1];
	if (sync.constructor != Number)
		return false;
	if (watching) {
		var post = posts[watching];
		if (post && !post.op)
			client.watching = watching;
		else
			return false;
	}
	if (sync == sync_number) {
		multisend(client, [[common.SYNCHRONIZE, sync_number]]);
		client.synced = true;
		return true; /* already synchronized */
	}
	if (sync > sync_number)
		return false; /* client in the future? */
	if (sync < backlog_last_dropped)
		return false; /* client took too long */
	var logs = [];
	for (var i = sync - backlog_last_dropped; i < backlog.length; i++) {
		var log = backlog[i];
		if (!watching || log[BL_THREAD] == watching)
			logs.push(log[BL_MSG]);
	}
	logs.push('[' + common.SYNCHRONIZE + ',' + sync_number + ']');
	client.socket.send('[' + logs.join() + ']');
	client.synced = true;
	return true;
}

post_env = {format_link: function (num, env) {
	var post = posts[num];
	if (post)
		env.callback(common.safe('>>' + num + ''));
	else
		env.callback('>>' + num);
}};

function write_thread_html(thread, response) {
	response.write('
\n');
	for (var i = 0; i < thread.length; i++)
		response.write(common.gen_post_html(thread[i], post_env));
	response.write('
\n');
}

var index_tmpl = jsontemplate.Template(fs.readFileSync('index.html', 'UTF-8')
		).expand(config).split(/\$[A-Z]+/);
var notfound_html = fs.readFileSync('www/404.html');

var http_headers = {'Content-Type': 'text/html; charset=UTF-8'};
var server = http.createServer(function(req, resp) {
	if (req.method.toLowerCase() == 'post') {
		handle_upload(req, resp);
		return;
	}
	if (req.url == '/' && render_index(req, resp))
		return;
	m = req.url.match(/^\/(\d+)$/);
	if (m && render_thread(req, resp, m[1]))
		return;
	resp.writeHead(404, http_headers);
	resp.end(notfound_html);
});

function render_index(req, resp) {
	resp.writeHead(200, http_headers);
	resp.write(index_tmpl[0]);
	resp.write(sync_number.toString());
	resp.write(index_tmpl[1]);
	for (var i = 0; i < threads.length; i++)
		write_thread_html(threads[i], resp);
	resp.end(index_tmpl[2]);
	return true;
}

function render_thread(req, resp, num) {
	var post = posts[parseInt(num)];
	if (!post)
		return false;
	if (post.op) {
		resp.writeHead(301, {Location: '/'+post.op+'#q'+post.num});
		resp.end();
		return true;
	}
	resp.writeHead(200, http_headers);
	resp.write(index_tmpl[0]);
	resp.write(sync_number.toString());
	resp.write(index_tmpl[1]);
	write_thread_html(post.thread, resp);
	resp.end(index_tmpl[2]);
	return true;
}

function plain(resp, code, text) {
	resp.writeHead(code, {'Content-Type': 'text/plain'});
	resp.end(text);
}

function handle_upload(req, resp) {
	var form = new formidable.IncomingForm();
	form.maxFieldsSize = 512;
	form.onPart = function (part) {
		if (part.filename && part.name == 'image')
			form.handlePart(part);
		else if (!part.filename && part.name == 'client_id')
			form.handlePart(part);
		else {
			this._error('Superfluous field.');
			if (part.path)
				console.log(part.path);
		}
	};
	form.parse(req, function (err, fields, files) {
		if (err) {
			console.log("Upload error: " + err);
			var code = 500;
			err = '' + (err.message || err);
			if (err.match(/exceed|parse/))
				code = 412;
			else if (err.match(/Superfluous/))
				code = 400;
			return plain(resp, code, err);
		}
		var image = files.image;
		if (!image)
			return plain(resp, 400, 'No image supplied.');
		var fail = function(code, text) {
			fs.unlink(image.path);
			plain(resp, code, text);
		}
		image.ext = path.extname(image.filename);
		if (config.IMAGE_EXTS.indexOf(image.ext.toLowerCase()) < 0)
			return fail(400, 'Invalid image format.');
		image.client_id = parseInt(fields.client_id);
		if (!(image.client_id in clients))
			return fail(401, 'Invalid client id.');
		plain(resp, 202, 'Processing ' + image.filename);
		resize_image(image, upload_image);
	});
}

function resize_image(image, callback) {
	image.thumb = image.path + '_thumb';
	/* TODO: check that it's actually one of the supported image types */
	exec('convert ' + image.path + ' -gamma 0.454545 -filter lanczos'
		+ ' -resize 50% -gamma 2.2 -quality ' + config.THUMB_QUALITY
		+ ' -sampling-factor 1x1 ' + image.thumb,
		image_handler(image, upload_image));
}

function image_handler(image, callback) {
	return function (error, stdout, stderr) {
		if (error != null)
			console.log(error);
		if (stdout)
			console.log(stdout);
		if (stderr)
			console.log(stderr);
		if (error == null && !stderr)
			callback(image);
		else {
			fs.unlink(image.path);
			fs.unlink(image.thumb);
		}
	};
}

function upload_image(image) {
	var base = new Date().getTime();
	var dest = path.join(config.IMAGE_DIR, base + image.ext);
	var thumb_dest = path.join(config.THUMB_DIR, base + '.jpg');
	exec('mv -- ' + image.path + ' ' + dest, image_handler(image,
	function (image) {
	exec('mv -- ' + image.thumb + ' ' + thumb_dest, image_handler(image,
	function (image) {
		console.log(image.client_id + ' successfully uploaded ' + dest);
	}));
	}));
}

function on_client (socket) {
	var id = socket.sessionId;
	var client = {id: id, socket: socket, post: null, synced: false,
			watching: null};
	clients[id] = client;
	socket.on('message', function (data) {
		msg = JSON.parse(data);
		var type = common.INVALID;
		if (client.post && msg.constructor == String)
			type = common.UPDATE_POST;
		else if (msg.constructor == Array)
			type = msg.shift();
		var func = dispatcher[type];
		if (!func || !func(msg, client)) {
			console.log("Got invalid message " + data);
			multisend(client, [[common.INVALID]]);
		}
	});
	socket.on('disconnect', function () {
		delete clients[id];
		if (client.post)
			finish_post(client.post, id);
	});
}

function is_integer(n) {
	return (typeof(n) == 'number' && parseFloat(n) == parseInt(n)
			&& !isNaN(n));
}

function validate(msg, schema) {
	if (msg == null || typeof(msg) != 'object')
		return false;
	for (var k in schema) {
		var m = msg[k];
		if (m == null || m.constructor != schema[k])
			return false;
		if (schema[k] == Number && !is_integer(m))
			return false;
	}
	return true;
}

function valid_links(frag, state) {
	var links = {};
	env = {callback: function (frag) {}, format_link: function (num, e) {
		var post = posts[num];
		if (post)
			links[num] = post.op || post.num;
	}};
	common.format_fragment(frag, state, env);
	return links;
}

/* TEMP: Must be a better way */
function empty(obj) {
	for (k in obj)
		return false;
	return true;
}

dispatcher[common.ALLOCATE_POST] = function (msg, client) {
	if (msg.length != 1)
		return false;
	msg = msg.shift();
	if (!validate(msg, {name: String, frag: String}))
		return false;
	if (!msg.frag.replace(/[ \n]/g, ''))
		return false;
	var post = {
		time: new Date().getTime(),
		editing: true,
		body: msg.frag
	};
	if (is_integer(msg.op) && posts[msg.op] && !posts[msg.op].op)
		post.op = msg.op;
	if (client.watching && client.watching != post.op)
		return false;
	var parsed = common.parse_name(msg.name);
	post.name = parsed[0];
	if (parsed[1] || parsed[2]) {
		var trip = tripcode.hash(parsed[1], parsed[2]);
		if (trip)
			post.trip = trip;
	}
	if (msg.email && msg.email.constructor == String)
		post.email = msg.email.trim().substr(0, 320);

	/* No going back now */
	post.num = post_counter++;
	posts[post.num] = post;
	var state = common.initial_post_state();
	var links = valid_links(post.body, state);
	if (!empty(links))
		post.links = links;
	broadcast([common.INSERT_POST, post], post, client.id);
	multisend(client, [[common.ALLOCATE_POST, post]]);
	/* Store some extra state for later */
	post.links = links;
	post.state = state;
	client.post = post;
	if (!post.op) {
		/* New thread */
		post.thread = [post];
		threads.unshift(post.thread);
	}
	else {
		var thread = posts[post.op].thread;
		thread.push(post);
 		if (post.email != 'sage') {
			/* Bump thread */
			for (var i = 0; i < threads.length; i++) {
				if (threads[i] == thread) {
					threads.splice(i, 1);
					threads.unshift(thread);
					break;
				}
			}
		}
	}
	return true;
}

dispatcher[common.UPDATE_POST] = function (frag, client) {
	if (!frag || frag.constructor != String)
		return false;
	var post = client.post;
	if (!post || !post.editing)
		return false;
	/* imporant: broadcast prior state */
	var msg = [common.UPDATE_POST, post.num, frag].concat(post.state);
	var links = valid_links(frag, post.state);
	if (!empty(links))
		msg.push({links: links});
	broadcast(msg, post, client.id);
	post.body += frag;
	for (var k in links)
		post.links[k] = links[k];
	return true;
}

function finish_post(post, owner_id) {
	broadcast([common.FINISH_POST, post.num], post, owner_id);
	post.editing = false;
	delete post.state;
}

dispatcher[common.FINISH_POST] = function (msg, client) {
	if (msg.length)
		return false;
	var post = client.post;
	if (!post || !post.editing)
		return false;
	finish_post(post, client.id);
	client.post = null;
	return true;
}

server.listen(config.PORT);
var listener = io.listen(server, {
	transports: ['websocket', 'server-events', 'htmlfile', 'xhr-multipart',
		'xhr-polling']
});
listener.on('connection', on_client);
<commit_msg>Add isEmpty<commit_after>var common = require('./common'),
	config = require('./config').config,
	exec = require('child_process').exec,
	formidable = require('../formidable'),
	fs = require('fs'),
	io = require('../socket.io'),
	jsontemplate = require('./json-template'),
	http = require('http'),
	path = require('path'),
	tripcode = require('./tripcode');

var threads = [];
var posts = {};
var post_counter = 1;
var clients = {};
var dispatcher = {};

var sync_number = 0;
var backlog = [];
var backlog_last_dropped = 0;
var BL_WHEN = 0, BL_MSG = 1, BL_THREAD = 2;

function multisend(client, msgs) {
	client.socket.send(JSON.stringify(msgs));
}

function broadcast(msg, post, origin) {
	var thread_num = post.op || post.num;
	++sync_number;
	msg = JSON.stringify(msg);
	var payload = '[' + msg + ']';
	for (id in clients) {
		var client = clients[id];
		if (client.watching && client.watching != thread_num)
			multisend(client, [[common.INVALID]]);
		else if (id == origin) {
			/* Client won't increment SYNC since they won't
			 * receive the broadcasted message, so do manually */
			multisend(client, [[common.SYNCHRONIZE, sync_number]]);
		}
		else if (client.synced)
			client.socket.send(payload);
	}
	var now = new Date().getTime();
	backlog.push([now, msg, thread_num]);
	cleanup_backlog(now);
}

function cleanup_backlog(now) {
	var limit = now - config.BACKLOG_PERIOD;
	/* binary search would be nice */
	while (backlog.length && backlog[0][BL_WHEN] < limit) {
		backlog.shift();
		backlog_last_dropped++;
	}
}

dispatcher[common.SYNCHRONIZE] = function (msg, client) {
	if (msg.length != 2)
		return false;
	var sync = msg[0], watching = msg[1];
	if (sync.constructor != Number)
		return false;
	if (watching) {
		var post = posts[watching];
		if (post && !post.op)
			client.watching = watching;
		else
			return false;
	}
	if (sync == sync_number) {
		multisend(client, [[common.SYNCHRONIZE, sync_number]]);
		client.synced = true;
		return true; /* already synchronized */
	}
	if (sync > sync_number)
		return false; /* client in the future? */
	if (sync < backlog_last_dropped)
		return false; /* client took too long */
	var logs = [];
	for (var i = sync - backlog_last_dropped; i < backlog.length; i++) {
		var log = backlog[i];
		if (!watching || log[BL_THREAD] == watching)
			logs.push(log[BL_MSG]);
	}
	logs.push('[' + common.SYNCHRONIZE + ',' + sync_number + ']');
	client.socket.send('[' + logs.join() + ']');
	client.synced = true;
	return true;
}

post_env = {format_link: function (num, env) {
	var post = posts[num];
	if (post)
		env.callback(common.safe('>>' + num + ''));
	else
		env.callback('>>' + num);
}};

function write_thread_html(thread, response) {
	response.write('
\n');
	for (var i = 0; i < thread.length; i++)
		response.write(common.gen_post_html(thread[i], post_env));
	response.write('
\n');
}

var index_tmpl = jsontemplate.Template(fs.readFileSync('index.html', 'UTF-8')
		).expand(config).split(/\$[A-Z]+/);
var notfound_html = fs.readFileSync('www/404.html');

var http_headers = {'Content-Type': 'text/html; charset=UTF-8'};
var server = http.createServer(function(req, resp) {
	if (req.method.toLowerCase() == 'post') {
		handle_upload(req, resp);
		return;
	}
	if (req.url == '/' && render_index(req, resp))
		return;
	m = req.url.match(/^\/(\d+)$/);
	if (m && render_thread(req, resp, m[1]))
		return;
	resp.writeHead(404, http_headers);
	resp.end(notfound_html);
});

function render_index(req, resp) {
	resp.writeHead(200, http_headers);
	resp.write(index_tmpl[0]);
	resp.write(sync_number.toString());
	resp.write(index_tmpl[1]);
	for (var i = 0; i < threads.length; i++)
		write_thread_html(threads[i], resp);
	resp.end(index_tmpl[2]);
	return true;
}

function render_thread(req, resp, num) {
	var post = posts[parseInt(num)];
	if (!post)
		return false;
	if (post.op) {
		resp.writeHead(301, {Location: '/'+post.op+'#q'+post.num});
		resp.end();
		return true;
	}
	resp.writeHead(200, http_headers);
	resp.write(index_tmpl[0]);
	resp.write(sync_number.toString());
	resp.write(index_tmpl[1]);
	write_thread_html(post.thread, resp);
	resp.end(index_tmpl[2]);
	return true;
}

function plain(resp, code, text) {
	resp.writeHead(code, {'Content-Type': 'text/plain'});
	resp.end(text);
}

function handle_upload(req, resp) {
	var form = new formidable.IncomingForm();
	form.maxFieldsSize = 512;
	form.onPart = function (part) {
		if (part.filename && part.name == 'image')
			form.handlePart(part);
		else if (!part.filename && part.name == 'client_id')
			form.handlePart(part);
		else {
			this._error('Superfluous field.');
			if (part.path)
				console.log(part.path);
		}
	};
	form.parse(req, function (err, fields, files) {
		if (err) {
			console.log("Upload error: " + err);
			var code = 500;
			err = '' + (err.message || err);
			if (err.match(/exceed|parse/))
				code = 412;
			else if (err.match(/Superfluous/))
				code = 400;
			return plain(resp, code, err);
		}
		var image = files.image;
		if (!image)
			return plain(resp, 400, 'No image supplied.');
		var fail = function(code, text) {
			fs.unlink(image.path);
			plain(resp, code, text);
		}
		image.ext = path.extname(image.filename);
		if (config.IMAGE_EXTS.indexOf(image.ext.toLowerCase()) < 0)
			return fail(400, 'Invalid image format.');
		image.client_id = parseInt(fields.client_id);
		if (!(image.client_id in clients))
			return fail(401, 'Invalid client id.');
		plain(resp, 202, 'Processing ' + image.filename);
		resize_image(image, upload_image);
	});
}

function resize_image(image, callback) {
	image.thumb = image.path + '_thumb';
	/* TODO: check that it's actually one of the supported image types */
	exec('convert ' + image.path + ' -gamma 0.454545 -filter lanczos'
		+ ' -resize 50% -gamma 2.2 -quality ' + config.THUMB_QUALITY
		+ ' -sampling-factor 1x1 ' + image.thumb,
		image_handler(image, upload_image));
}

function image_handler(image, callback) {
	return function (error, stdout, stderr) {
		if (error != null)
			console.log(error);
		if (stdout)
			console.log(stdout);
		if (stderr)
			console.log(stderr);
		if (error == null && !stderr)
			callback(image);
		else {
			fs.unlink(image.path);
			fs.unlink(image.thumb);
		}
	};
}

function upload_image(image) {
	var base = new Date().getTime();
	var dest = path.join(config.IMAGE_DIR, base + image.ext);
	var thumb_dest = path.join(config.THUMB_DIR, base + '.jpg');
	exec('mv -- ' + image.path + ' ' + dest, image_handler(image,
	function (image) {
	exec('mv -- ' + image.thumb + ' ' + thumb_dest, image_handler(image,
	function (image) {
		console.log(image.client_id + ' successfully uploaded ' + dest);
	}));
	}));
}

function on_client (socket) {
	var id = socket.sessionId;
	var client = {id: id, socket: socket, post: null, synced: false,
			watching: null};
	clients[id] = client;
	socket.on('message', function (data) {
		msg = JSON.parse(data);
		var type = common.INVALID;
		if (client.post && msg.constructor == String)
			type = common.UPDATE_POST;
		else if (msg.constructor == Array)
			type = msg.shift();
		var func = dispatcher[type];
		if (!func || !func(msg, client)) {
			console.log("Got invalid message " + data);
			multisend(client, [[common.INVALID]]);
		}
	});
	socket.on('disconnect', function () {
		delete clients[id];
		if (client.post)
			finish_post(client.post, id);
	});
}

function is_integer(n) {
	return (typeof(n) == 'number' && parseFloat(n) == parseInt(n)
			&& !isNaN(n));
}

function validate(msg, schema) {
	if (msg == null || typeof(msg) != 'object')
		return false;
	for (var k in schema) {
		var m = msg[k];
		if (m == null || m.constructor != schema[k])
			return false;
		if (schema[k] == Number && !is_integer(m))
			return false;
	}
	return true;
}

function valid_links(frag, state) {
	var links = {};
	env = {callback: function (frag) {}, format_link: function (num, e) {
		var post = posts[num];
		if (post)
			links[num] = post.op || post.num;
	}};
	common.format_fragment(frag, state, env);
	return links;
}

function isEmpty(obj) {
	for (k in obj)
		if (obj.hasOwnProperty(k))
			return false;
	return true;
}

dispatcher[common.ALLOCATE_POST] = function (msg, client) {
	if (msg.length != 1)
		return false;
	msg = msg.shift();
	if (!validate(msg, {name: String, frag: String}))
		return false;
	if (!msg.frag.replace(/[ \n]/g, ''))
		return false;
	var post = {
		time: new Date().getTime(),
		editing: true,
		body: msg.frag
	};
	if (is_integer(msg.op) && posts[msg.op] && !posts[msg.op].op)
		post.op = msg.op;
	if (client.watching && client.watching != post.op)
		return false;
	var parsed = common.parse_name(msg.name);
	post.name = parsed[0];
	if (parsed[1] || parsed[2]) {
		var trip = tripcode.hash(parsed[1], parsed[2]);
		if (trip)
			post.trip = trip;
	}
	if (msg.email && msg.email.constructor == String)
		post.email = msg.email.trim().substr(0, 320);

	/* No going back now */
	post.num = post_counter++;
	posts[post.num] = post;
	var state = common.initial_post_state();
	var links = valid_links(post.body, state);
	if (!isEmpty(links))
		post.links = links;
	broadcast([common.INSERT_POST, post], post, client.id);
	multisend(client, [[common.ALLOCATE_POST, post]]);
	/* Store some extra state for later */
	post.links = links;
	post.state = state;
	client.post = post;
	if (!post.op) {
		/* New thread */
		post.thread = [post];
		threads.unshift(post.thread);
	}
	else {
		var thread = posts[post.op].thread;
		thread.push(post);
 		if (post.email != 'sage') {
			/* Bump thread */
			for (var i = 0; i < threads.length; i++) {
				if (threads[i] == thread) {
					threads.splice(i, 1);
					threads.unshift(thread);
					break;
				}
			}
		}
	}
	return true;
}

dispatcher[common.UPDATE_POST] = function (frag, client) {
	if (!frag || frag.constructor != String)
		return false;
	var post = client.post;
	if (!post || !post.editing)
		return false;
	/* imporant: broadcast prior state */
	var msg = [common.UPDATE_POST, post.num, frag].concat(post.state);
	var links = valid_links(frag, post.state);
	if (!isEmpty(links))
		msg.push({links: links});
	broadcast(msg, post, client.id);
	post.body += frag;
	for (var k in links)
		post.links[k] = links[k];
	return true;
}

function finish_post(post, owner_id) {
	broadcast([common.FINISH_POST, post.num], post, owner_id);
	post.editing = false;
	delete post.state;
}

dispatcher[common.FINISH_POST] = function (msg, client) {
	if (msg.length)
		return false;
	var post = client.post;
	if (!post || !post.editing)
		return false;
	finish_post(post, client.id);
	client.post = null;
	return true;
}

server.listen(config.PORT);
var listener = io.listen(server, {
	transports: ['websocket', 'server-events', 'htmlfile', 'xhr-multipart',
		'xhr-polling']
});
listener.on('connection', on_client);
<|endoftext|>


<commit_before>'use strict';

var chalk = require('chalk');
var spawn = require('hexo-util/lib/spawn');

module.exports = function(args) {
  if (!args.host || !args.user || !args.root) {
    var help = '';

    help += 'You should configure deployment settings in _config.yml first!\n\n';
    help += 'Example:\n';
    help += '  deploy:\n';
    help += '    type: rsync\n';
    help += '    host: <host>\n';
    help += '    user: <user>\n';
    help += '    root: <root>\n';
    help += '    port: [port] # Default is 22\n';
    help += '    delete: [true|false] # Default is true\n';
    help += '    args: <rsync args>\n';
    help += '    verbose: [true|false] # Default is true\n';
    help += '    ignore_errors: [true|false] # Default is false\n\n';
    help += 'For more help, you can check the docs: ' + chalk.underline('http://hexo.io/docs/deployment.html');

    console.log(help);
    return;
  }

  if (!args.hasOwnProperty('delete')) args.delete = true;
  if (!args.hasOwnProperty('verbose')) args.verbose = true;
  if (!args.hasOwnProperty('ignore_errors')) args.ignore_errors = false;

  var params = [
    '-az',
    this.public_dir,
    args.user + '@' + args.host + ':' + args.root
  ];

  if (args.port && args.port > 0 && args.port < 65536){
     params.splice(params.length - 2, 0, '-e');
     params.splice(params.length - 2, 0, 'ssh -p ' + args.port);
  }
  if (args.verbose) params.unshift('-v');
  if (args.ignore_errors) params.unshift('--ignore-errors');
  if (args.delete) params.unshift('--delete');
  if (args.args) params.unshift(args.args);

  return spawn('rsync', params, {verbose: true});
};
<commit_msg>Fix wrong public path for w32<commit_after>'use strict';

var chalk = require('chalk');
var spawn = require('hexo-util/lib/spawn');
var pathFn = require('path');

module.exports = function(args) {
  if (!args.host || !args.user || !args.root) {
    var help = '';

    help += 'You should configure deployment settings in _config.yml first!\n\n';
    help += 'Example:\n';
    help += '  deploy:\n';
    help += '    type: rsync\n';
    help += '    host: <host>\n';
    help += '    user: <user>\n';
    help += '    root: <root>\n';
    help += '    port: [port] # Default is 22\n';
    help += '    delete: [true|false] # Default is true\n';
    help += '    args: <rsync args>\n';
    help += '    verbose: [true|false] # Default is true\n';
    help += '    ignore_errors: [true|false] # Default is false\n\n';
    help += 'For more help, you can check the docs: ' + chalk.underline('http://hexo.io/docs/deployment.html');

    console.log(help);
    return;
  }

  if (!args.hasOwnProperty('delete')) args.delete = true;
  if (!args.hasOwnProperty('verbose')) args.verbose = true;
  if (!args.hasOwnProperty('ignore_errors')) args.ignore_errors = false;

  var params = [
    '-az',
    process.platform === 'win32' ? pathFn.basename(this.public_dir) : this.public_dir,
    args.user + '@' + args.host + ':' + args.root
  ];

  if (args.port && args.port > 0 && args.port < 65536){
     params.splice(params.length - 2, 0, '-e');
     params.splice(params.length - 2, 0, 'ssh -p ' + args.port);
  }
  if (args.verbose) params.unshift('-v');
  if (args.ignore_errors) params.unshift('--ignore-errors');
  if (args.delete) params.unshift('--delete');
  if (args.args) params.unshift(args.args);

  return spawn('rsync', params, {verbose: true});
};
<|endoftext|>


<commit_before>import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { browserHistory } from 'react-router';
import configureStore from './configure-store';
import { syncHistoryWithStore } from 'react-router-redux';

export default ({ createRoutes, reducers }) => {
  const store = configureStore(reducers);
  const history = syncHistoryWithStore(browserHistory, store);
  const routes = createRoutes(history);

  render(
    <Provider store={store}>
      { routes }
    </Provider>,
    document.getElementById('root')
  );

  return store;
};
<commit_msg>Hot reloading fix<commit_after>import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { browserHistory } from 'react-router';
import configureStore from './configure-store';
import { syncHistoryWithStore } from 'react-router-redux';

export default ({ createRoutes, reducers }) => {
  const store = configureStore(reducers);
  const history = syncHistoryWithStore(browserHistory, store);
  const routes = createRoutes(history);

  const app = render(
    <Provider store={store}>
      { routes }
    </Provider>,
    document.getElementById('root')
  );

  if (module.hot) {
    require('react-hot-loader/Injection').RootInstanceProvider.injectProvider({
      getRootInstances() {
        return [app];
      },
    });
  }

  return { store, app };
};
<|endoftext|>


<commit_before>import ChildProcess from 'child_process'
import EventEmitter from 'events'
import Crypto from 'crypto-js'
import Rand from 'crypto-rand'
import fs from 'fs'

export default class Peer extends EventEmitter {
  constructor (props) {
    super()
    props = props || {}
    this.uid = this.generateUid()
    this.magnet = props.magnet || props.link
    this.metadata = {}
    this.started = true
    this.idle = {
      date: new Date(),
      timeout: 1000
    }

    if (this.magnet) {
      this.child = ChildProcess.fork(`${__dirname}/module/torrentWorker`, [this.magnet])

      this.child.on('message', (message) => this.handleMessage(JSON.parse(message)))
      this.child.on('close', (code) => this.handleMessage({
        type: code === 0 ? 'done' : 'error'
      }))
    } else {
      this.emit('err', {
        code: 404,
        message: 'No magnet or link provided.'
      })
    }
  }

  stop () {
    if (this.child) {
      this.child.send(JSON.stringify({
        type: 'stop'
      }))
    }
  }

  handleMessage (message) {
    if (!this.started) {
      return
    }
    this.metadata = message.metadata
    switch (message.type) {
      case 'metadata':
        this.emit('metadata', this)
        break
      case 'download':
        if (new Date() - this.idle.date >= this.idle.timeout) {
          this.idle.date = new Date()
          this.emit('download', this)
        }
        break
      case 'done':
        this.started = false
        this.emit('done', this)
        break
      case 'stop':
        this.started = false
        this.emit('stop', this)
        break
      case 'noPeers':
        this.emit('noPeers', this)
        break
      case 'error':
        this.started = false
        this.emit('error', this)
        break
      default:
        break
    }
  }

  generateUid () {
    let seed = `${Rand.rand().toString()}`
    let longUid = Crypto.SHA256(seed).toString()
    return longUid.substr(0, 10)
  }

  toJSON () {
    var metadata = Object.assign({}, this.metadata)
    delete metadata.path
    delete metadata.fullPath

    return {
      magnet: this.magnet,
      metadata: metadata,
      uid: this.uid,
      url: `/peer/${this.uid}`
    }
  }

  cleanup () {
    fs.unlink(this.magnet, () => {})
  }
}
<commit_msg>Wait 30 seconds before emitting done<commit_after>import ChildProcess from 'child_process'
import EventEmitter from 'events'
import Crypto from 'crypto-js'
import Rand from 'crypto-rand'
import fs from 'fs'

export default class Peer extends EventEmitter {
  constructor (props) {
    super()
    props = props || {}
    this.uid = this.generateUid()
    this.magnet = props.magnet || props.link
    this.metadata = {}
    this.started = true
    this.idle = {
      date: new Date(),
      timeout: 1000
    }

    if (this.magnet) {
      this.child = ChildProcess.fork(`${__dirname}/module/torrentWorker`, [this.magnet])

      this.child.on('message', (message) => this.handleMessage(JSON.parse(message)))
      this.child.on('close', (code) => this.handleMessage({
        type: code === 0 ? 'done' : 'error'
      }))
    } else {
      this.emit('err', {
        code: 404,
        message: 'No magnet or link provided.'
      })
    }
  }

  stop () {
    if (this.child) {
      this.child.send(JSON.stringify({
        type: 'stop'
      }))
    }
  }

  handleMessage (message) {
    if (!this.started) {
      return
    }
    this.metadata = message.metadata
    switch (message.type) {
      case 'metadata':
        this.emit('metadata', this)
        break
      case 'download':
        if (new Date() - this.idle.date >= this.idle.timeout) {
          this.idle.date = new Date()
          this.emit('download', this)
        }
        break
      case 'done':
        this.started = false
        setTimeout(() => this.emit('done', this), 30000)
        break
      case 'stop':
        this.started = false
        this.emit('stop', this)
        break
      case 'noPeers':
        this.emit('noPeers', this)
        break
      case 'error':
        this.started = false
        this.emit('error', this)
        break
      default:
        break
    }
  }

  generateUid () {
    let seed = `${Rand.rand().toString()}`
    let longUid = Crypto.SHA256(seed).toString()
    return longUid.substr(0, 10)
  }

  toJSON () {
    var metadata = Object.assign({}, this.metadata)
    delete metadata.path
    delete metadata.fullPath

    return {
      magnet: this.magnet,
      metadata: metadata,
      uid: this.uid,
      url: `/peer/${this.uid}`
    }
  }

  cleanup () {
    fs.unlink(this.magnet, () => {})
  }
}
<|endoftext|>


<commit_before>var saving = {
  todo: {
    html: false,
    css: false,
    javascript: false
  },
  _inprogress: false,
  inprogress: function (inprogress) {
    if (typeof inprogress === 'undefined') {
      return saving._inprogress;
    }

    saving._inprogress = inprogress;
    if (inprogress === false) {
      var panels = ['html','css','javascript'],
          todo;

      var save = function () {
        var todo = panels.pop();
        if (todo && saving.todo[todo]) {
          saving._inprogress = true;
          updateCode(todo, save);
          saving.todo[todo] = false;
        } else if (todo) {
          save();
        }
      };

      save();
    }
  }
};

// to allow for download button to be introduced via beta feature
$('a.save').click(function (event) {
  event.preventDefault();

  analytics.milestone();
  // if save is disabled, hitting save will trigger a reload
  var ajax = true;
  if (jsbin.saveDisabled === true) {
    ajax = false;
  }
  saveCode('save', ajax);

  return false;
});

var $shareLinks = $('#share .link');

$panelCheckboxes = $('#sharemenu #sharepanels input');

function updateSavedState() {
  var mapping = {
    live: 'output',
    javascript: 'js',
    css: 'css',
    html: 'html',
    console: 'console'
  };
  var query = $panelCheckboxes.filter(':checked').map(function () {
    return mapping[this.getAttribute('data-panel')];
  }).get().join(',');
  $shareLinks.each(function () {
    var path = this.getAttribute('data-path');
    var url = jsbin.getURL(false, path === '/') + path + (query && this.id !== 'livepreview' ? '?' + query : ''),
        nodeName = this.nodeName;
    if (nodeName === 'A') {
      this.href = url;
    } else if (nodeName === 'INPUT') {
      this.value = url;
    } else if (nodeName === 'TEXTAREA') {
      this.value = ('' + documentTitle + '<' + 'script src="' + jsbin.static + '/js/embed.js"><' + '/script>').replace(/<>"&/g, function (m) {
          return {
            '<': '<',
            '>': '>',
            '"': '"',
            '&': '&'
          }[m];
        });
    }
  });
}

$('#sharemenu').bind('open', function () {
  updateSavedState();
});

$document.on('saved', function () {
  updateSavedState();
  $shareLinks.closest('.menu').removeClass('hidden');

  $('#jsbinurl').attr('href', jsbin.getURL()).removeClass('hidden');
  $('#clone').removeClass('hidden');
});

var saveChecksum = jsbin.state.checksum || sessionStorage.getItem('checksum') || false;

// store it back on state
jsbin.state.checksum = saveChecksum;

if (saveChecksum) {
  // remove the disabled class, but also remove the cancelling event handlers
  $('#share div.disabled').removeClass('disabled').unbind('click mousedown mouseup');
} else {
  $('#share div.disabled').one('click', function (event) {
    event.preventDefault();
    $('a.save').click();
  });
}

$document.one('saved', function () {
  $('#share div.disabled').removeClass('disabled').unbind('click mousedown mouseup');
});

function onSaveError(jqXHR, panelId) {
  if (jqXHR.status === 413) {
    // Hijack the tip label to show an error message.
    $('#tip p').html('Sorry this bin is too large for us to save');
    $(document.documentElement).addClass('showtip');
  } else if (jqXHR.status === 403) {
    $document.trigger('tip', {
      type: 'error',
      content: 'I think there\'s something wrong with your session and I\'m unable to save. Refresh to fix this, you will not lose your code.'
    });
  } else if (panelId) {
    if (panelId) savingLabels[panelId].text('Saving...').animate({ opacity: 1 }, 100);
    window._console.error({message: 'Warning: Something went wrong while saving. Your most recent work is not saved.'});
  }
}

// only start live saving it they're allowed to (whereas save is disabled if they're following)
if (!jsbin.saveDisabled) {
  $('.code.panel .label .name').append('Saved');

  var savingLabels = {
    html: $('.panel.html .name span'),
    javascript: $('.panel.javascript .name span'),
    css: $('.panel.css .name span')
  };

  $document.bind('jsbinReady', function () {
    jsbin.panels.allEditors(function (panel) {
      panel.on('processor', function () {
        // if the url doesn't match the root - i.e. they've actually saved something then save on processor change
        if (jsbin.root !== jsbin.getURL()) {
          $document.trigger('codeChange', [{ panelId: panel.id }]);
        }
      });
    });

    $document.bind('codeChange', function (event, data) {
      // savingLabels[data.panelId].text('Saving');
      if (savingLabels[data.panelId]) {
        savingLabels[data.panelId].css({ 'opacity': 0 }).stop(true, true);
      }
    });

    $document.bind('saveComplete', throttle(function (event, data) {
      // show saved, then revert out animation
      savingLabels[data.panelId]
        .text('Saved')
        .stop(true, true)
        .animate({ opacity: 1 }, 100)
        .delay(1200)
        .animate({ opacity: 0 }, 500);
    }, 500));

    $document.bind('codeChange', throttle(function (event, data) {
      if (!data.panelId) {
        return;
      }

      if (jsbin.state.deleted) {
        return;
      }

      var panelId = data.panelId;

      jsbin.panels.savecontent();

      if (saving.inprogress()) {
        // queue up the request and wait
        saving.todo[panelId] = true;
        return;
      }

      saving.inprogress(true);

      // We force a full save if there's no checksum OR if there's no bin code/url
      if (!saveChecksum || !jsbin.state.code) {
        // create the bin and when the response comes back update the url
        saveCode('save', true);
      } else {
        updateCode(panelId);
      }
    }, 250));
  });
} else {
  $document.one('jsbinReady', function () {
    var shown = false;
    if (!jsbin.embed && !jsbin.sandbox) {
      $document.on('codeChange', function (event, data) {
        if (!data.onload && !shown && data.origin !== 'setValue') {
          shown = true;
          var ismac = navigator.userAgent.indexOf(' Mac ') !== -1;
          var cmd = ismac ? '⌘' : 'ctrl';
          var shift = ismac ? '⇧' : 'shift';
          var plus = ismac ? '' : '+';

          $document.trigger('tip', {
            type: 'notification',
            content: 'You\'re currently viewing someone else\'s live stream, but you can clone your own copy (' + cmd + plus + shift + plus + 'S) at any time to save your edits'
          });
        }
      });
    }
  });
}

function compressData (keys, obj) {
  obj.compressed = keys;
  keys.split(',').forEach(function (key) {
    obj[key] = LZString.compress(obj[key]);
  });
}

function updateCode(panelId, callback) {
  var panelSettings = {};

  if (jsbin.state.processors) {
    panelSettings.processors = jsbin.state.processors;
  }

  var data = {
    code: jsbin.state.code,
    revision: jsbin.state.revision,
    method: 'update',
    panel: panelId,
    content: editors[panelId].getCode(),
    checksum: saveChecksum,
    settings: JSON.stringify(panelSettings),
  };

  if (jsbin.settings.useCompression) {
    compressData('content', data);
  }

  $.ajax({
    url: jsbin.getURL() + '/save',
    data: data,
    type: 'post',
    dataType: 'json',
    headers: {'Accept': 'application/json'},
    success: function (data) {
      $document.trigger('saveComplete', { panelId: panelId });
      if (data.error) {
        saveCode('save', true, function (data) {
          // savedAlready = data.checksum;
        });
      }
    },
    error: function (jqXHR) {
      onSaveError(jqXHR, panelId);
    },
    complete: function () {
      saving.inprogress(false);
      callback && callback();
    }
  });
}

$('a.clone').click(function (event) {
  event.preventDefault();

  // save our panel layout - assumes our user is happy with this layout
  jsbin.panels.save();
  analytics.clone();

  var $form = setupform('save,new');
  $form.submit();

  return false;
});

function setupform(method) {
var $form = $('form#saveform').empty()
    .append('<input type="hidden" name="javascript" />')
    .append('<input type="hidden" name="html" />')
    .append('<input type="hidden" name="css" />')
    .append('<input type="hidden" name="method" />')
    .append('<input type="hidden" name="_csrf" value="' + jsbin.state.token + '" />')
    .append('<input type="hidden" name="settings" />');

  var settings = {};

  if (jsbin.state.processors) {
    settings.processors = jsbin.state.processors;
  }

  // this prevents new revisions forking off the welcome bin
  // because it's looking silly!
  if (jsbin.state.code === 'welcome') {
    $form.attr('action', '/save');
  }

  $form.find('input[name=settings]').val(JSON.stringify(settings));
  $form.find('input[name=javascript]').val(editors.javascript.getCode());
  $form.find('input[name=css]').val(editors.css.getCode());
  $form.find('input[name=html]').val(editors.html.getCode());
  $form.find('input[name=method]').val(method);

  return $form;
}

function pad(n){
  return n<10 ? '0'+n : n
}

function ISODateString(d){
  return d.getFullYear()+'-'
    + pad(d.getMonth()+1)+'-'
    + pad(d.getDate())+'T'
    + pad(d.getHours())+':'
    + pad(d.getMinutes())+':'
    + pad(d.getSeconds())+'Z'
}

function saveCode(method, ajax, ajaxCallback) {
  // create form and post to it
  var $form = setupform(method);
  // save our panel layout - assumes our user is happy with this layout
  jsbin.panels.save();
  jsbin.panels.saveOnExit = true;

  var data = (function () {
    var obj = {};
    $form.serializeArray().forEach(function (data) {
      obj[data.name] = data.value;
    });
    return obj;
  }());

  if (ajax) {
    $.ajax({
      url: $form.attr('action'),
      data: data,
      dataType: 'json',
      type: 'post',
      headers: {'Accept': 'application/json'},
      success: function (data) {
        var $binGroup,
            edit;

        $form.attr('action', data.url + '/save');
        if (ajaxCallback) {
          ajaxCallback(data);
        }

        sessionStorage.setItem('checksum', data.checksum);
        saveChecksum = data.checksum;

        jsbin.state.checksum = saveChecksum;
        jsbin.state.code = data.code;
        jsbin.state.revision = data.revision;
        jsbin.state.metadata = { name: jsbin.user.name };

        // getURL(true) gets the jsbin without the root attached
        // $binGroup = $('#history tr[data-url="' + jsbin.getURL(true) + '"]');
        // edit = data.edit.replace(location.protocol + '//' + window.location.host, '') + window.location.search;
        // $binGroup.find('td.url a span.first').removeClass('first');
        // $binGroup.before('
' + data.code + '/' + data.revision + '/	Just now	' + data.title + '
');

        if (window.history && window.history.pushState) {
          // updateURL(edit);
          window.history.pushState(null, '', jsbin.getURL() + '/edit');
          sessionStorage.setItem('url', jsbin.getURL());
        } else {
          window.location.hash = data.edit;
        }

        $document.trigger('saved');
      },
      error: function (jqXHR) {
        onSaveError(jqXHR, null);
      },
      complete: function () {
        saving.inprogress(false);
      }
    });
  } else {
    $form.submit();
  }
}

/**
 * Returns the similar part of two strings
 * @param  {String} a first string
 * @param  {String} b second string
 * @return {String}   common substring
 */
function sameStart(a, b) {
  if (a == b) return a;

  var tmp = b.slice(0, 1);
  while (a.indexOf(b.slice(0, tmp.length + 1)) === 0) {
    tmp = b.slice(0, tmp.length + 1);
  }

  return tmp;
}

/*

// refresh the window when we popstate, because for now we don't do an xhr to
// inject the panel content...yet.
window.onpopstate = function onpopstate(event) {
  // ignore the first popstate event, because that comes from the browser...
  if (!onpopstate.first) window.location.reload();
  else onpopstate.first = false;
};

onpopstate.first = true;

function updateURL(path) {
  var old = location.pathname,
      back = true,
      same = sameStart(old, path);
      sameAt = same.length;

  if (updateURL.timer) window.cancelAnimationFrame(updateURL.timer);

  var run = function () {
    if (location.pathname !== path) {
      updateURL.timer = window.requestAnimationFrame(run);
    }

    if (location.pathname !== same) {
      if (back) {
        history.replaceState({ path: path }, '', location.pathname.slice(0, -1));
      } else {
        history.replaceState({ path: path }, '', path.slice(0, location.pathname.length + 1));
      }
    } else {
      back = false;
      history.replaceState({ path: path }, '', path.slice(0, sameAt + 2));
    }
  };

  history.pushState({ path: path }, '', location.pathname.slice(0, -1));

  run();
}

*/
<commit_msg>Conditionally compress data for POSTs to /save<commit_after>var saving = {
  todo: {
    html: false,
    css: false,
    javascript: false
  },
  _inprogress: false,
  inprogress: function (inprogress) {
    if (typeof inprogress === 'undefined') {
      return saving._inprogress;
    }

    saving._inprogress = inprogress;
    if (inprogress === false) {
      var panels = ['html','css','javascript'],
          todo;

      var save = function () {
        var todo = panels.pop();
        if (todo && saving.todo[todo]) {
          saving._inprogress = true;
          updateCode(todo, save);
          saving.todo[todo] = false;
        } else if (todo) {
          save();
        }
      };

      save();
    }
  }
};

// to allow for download button to be introduced via beta feature
$('a.save').click(function (event) {
  event.preventDefault();

  analytics.milestone();
  // if save is disabled, hitting save will trigger a reload
  var ajax = true;
  if (jsbin.saveDisabled === true) {
    ajax = false;
  }
  saveCode('save', ajax);

  return false;
});

var $shareLinks = $('#share .link');

$panelCheckboxes = $('#sharemenu #sharepanels input');

function updateSavedState() {
  var mapping = {
    live: 'output',
    javascript: 'js',
    css: 'css',
    html: 'html',
    console: 'console'
  };
  var query = $panelCheckboxes.filter(':checked').map(function () {
    return mapping[this.getAttribute('data-panel')];
  }).get().join(',');
  $shareLinks.each(function () {
    var path = this.getAttribute('data-path');
    var url = jsbin.getURL(false, path === '/') + path + (query && this.id !== 'livepreview' ? '?' + query : ''),
        nodeName = this.nodeName;
    if (nodeName === 'A') {
      this.href = url;
    } else if (nodeName === 'INPUT') {
      this.value = url;
    } else if (nodeName === 'TEXTAREA') {
      this.value = ('' + documentTitle + '<' + 'script src="' + jsbin.static + '/js/embed.js"><' + '/script>').replace(/<>"&/g, function (m) {
          return {
            '<': '<',
            '>': '>',
            '"': '"',
            '&': '&'
          }[m];
        });
    }
  });
}

$('#sharemenu').bind('open', function () {
  updateSavedState();
});

$document.on('saved', function () {
  updateSavedState();
  $shareLinks.closest('.menu').removeClass('hidden');

  $('#jsbinurl').attr('href', jsbin.getURL()).removeClass('hidden');
  $('#clone').removeClass('hidden');
});

var saveChecksum = jsbin.state.checksum || sessionStorage.getItem('checksum') || false;

// store it back on state
jsbin.state.checksum = saveChecksum;

if (saveChecksum) {
  // remove the disabled class, but also remove the cancelling event handlers
  $('#share div.disabled').removeClass('disabled').unbind('click mousedown mouseup');
} else {
  $('#share div.disabled').one('click', function (event) {
    event.preventDefault();
    $('a.save').click();
  });
}

$document.one('saved', function () {
  $('#share div.disabled').removeClass('disabled').unbind('click mousedown mouseup');
});

function onSaveError(jqXHR, panelId) {
  if (jqXHR.status === 413) {
    // Hijack the tip label to show an error message.
    $('#tip p').html('Sorry this bin is too large for us to save');
    $(document.documentElement).addClass('showtip');
  } else if (jqXHR.status === 403) {
    $document.trigger('tip', {
      type: 'error',
      content: 'I think there\'s something wrong with your session and I\'m unable to save. Refresh to fix this, you will not lose your code.'
    });
  } else if (panelId) {
    if (panelId) savingLabels[panelId].text('Saving...').animate({ opacity: 1 }, 100);
    window._console.error({message: 'Warning: Something went wrong while saving. Your most recent work is not saved.'});
  }
}

// only start live saving it they're allowed to (whereas save is disabled if they're following)
if (!jsbin.saveDisabled) {
  $('.code.panel .label .name').append('Saved');

  var savingLabels = {
    html: $('.panel.html .name span'),
    javascript: $('.panel.javascript .name span'),
    css: $('.panel.css .name span')
  };

  $document.bind('jsbinReady', function () {
    jsbin.panels.allEditors(function (panel) {
      panel.on('processor', function () {
        // if the url doesn't match the root - i.e. they've actually saved something then save on processor change
        if (jsbin.root !== jsbin.getURL()) {
          $document.trigger('codeChange', [{ panelId: panel.id }]);
        }
      });
    });

    $document.bind('codeChange', function (event, data) {
      // savingLabels[data.panelId].text('Saving');
      if (savingLabels[data.panelId]) {
        savingLabels[data.panelId].css({ 'opacity': 0 }).stop(true, true);
      }
    });

    $document.bind('saveComplete', throttle(function (event, data) {
      // show saved, then revert out animation
      savingLabels[data.panelId]
        .text('Saved')
        .stop(true, true)
        .animate({ opacity: 1 }, 100)
        .delay(1200)
        .animate({ opacity: 0 }, 500);
    }, 500));

    $document.bind('codeChange', throttle(function (event, data) {
      if (!data.panelId) {
        return;
      }

      if (jsbin.state.deleted) {
        return;
      }

      var panelId = data.panelId;

      jsbin.panels.savecontent();

      if (saving.inprogress()) {
        // queue up the request and wait
        saving.todo[panelId] = true;
        return;
      }

      saving.inprogress(true);

      // We force a full save if there's no checksum OR if there's no bin code/url
      if (!saveChecksum || !jsbin.state.code) {
        // create the bin and when the response comes back update the url
        saveCode('save', true);
      } else {
        updateCode(panelId);
      }
    }, 250));
  });
} else {
  $document.one('jsbinReady', function () {
    var shown = false;
    if (!jsbin.embed && !jsbin.sandbox) {
      $document.on('codeChange', function (event, data) {
        if (!data.onload && !shown && data.origin !== 'setValue') {
          shown = true;
          var ismac = navigator.userAgent.indexOf(' Mac ') !== -1;
          var cmd = ismac ? '⌘' : 'ctrl';
          var shift = ismac ? '⇧' : 'shift';
          var plus = ismac ? '' : '+';

          $document.trigger('tip', {
            type: 'notification',
            content: 'You\'re currently viewing someone else\'s live stream, but you can clone your own copy (' + cmd + plus + shift + plus + 'S) at any time to save your edits'
          });
        }
      });
    }
  });
}

function compressData (keys, obj) {
  obj.compressed = keys;
  keys.split(',').forEach(function (key) {
    obj[key] = LZString.compress(obj[key]);
  });
}

function updateCode(panelId, callback) {
  var panelSettings = {};

  if (jsbin.state.processors) {
    panelSettings.processors = jsbin.state.processors;
  }

  var data = {
    code: jsbin.state.code,
    revision: jsbin.state.revision,
    method: 'update',
    panel: panelId,
    content: editors[panelId].getCode(),
    checksum: saveChecksum,
    settings: JSON.stringify(panelSettings),
  };

  if (jsbin.settings.useCompression) {
    compressData('content', data);
  }

  $.ajax({
    url: jsbin.getURL() + '/save',
    data: data,
    type: 'post',
    dataType: 'json',
    headers: {'Accept': 'application/json'},
    success: function (data) {
      $document.trigger('saveComplete', { panelId: panelId });
      if (data.error) {
        saveCode('save', true, function (data) {
          // savedAlready = data.checksum;
        });
      }
    },
    error: function (jqXHR) {
      onSaveError(jqXHR, panelId);
    },
    complete: function () {
      saving.inprogress(false);
      callback && callback();
    }
  });
}

$('a.clone').click(function (event) {
  event.preventDefault();

  // save our panel layout - assumes our user is happy with this layout
  jsbin.panels.save();
  analytics.clone();

  var $form = setupform('save,new');
  $form.submit();

  return false;
});

function setupform(method) {
var $form = $('form#saveform').empty()
    .append('<input type="hidden" name="javascript" />')
    .append('<input type="hidden" name="html" />')
    .append('<input type="hidden" name="css" />')
    .append('<input type="hidden" name="method" />')
    .append('<input type="hidden" name="_csrf" value="' + jsbin.state.token + '" />')
    .append('<input type="hidden" name="settings" />');

  var settings = {};

  if (jsbin.state.processors) {
    settings.processors = jsbin.state.processors;
  }

  // this prevents new revisions forking off the welcome bin
  // because it's looking silly!
  if (jsbin.state.code === 'welcome') {
    $form.attr('action', '/save');
  }

  $form.find('input[name=settings]').val(JSON.stringify(settings));
  $form.find('input[name=javascript]').val(editors.javascript.getCode());
  $form.find('input[name=css]').val(editors.css.getCode());
  $form.find('input[name=html]').val(editors.html.getCode());
  $form.find('input[name=method]').val(method);

  return $form;
}

function pad(n){
  return n<10 ? '0'+n : n
}

function ISODateString(d){
  return d.getFullYear()+'-'
    + pad(d.getMonth()+1)+'-'
    + pad(d.getDate())+'T'
    + pad(d.getHours())+':'
    + pad(d.getMinutes())+':'
    + pad(d.getSeconds())+'Z'
}

function saveCode(method, ajax, ajaxCallback) {
  // create form and post to it
  var $form = setupform(method);
  // save our panel layout - assumes our user is happy with this layout
  jsbin.panels.save();
  jsbin.panels.saveOnExit = true;

  var data = (function () {
    var obj = {};
    $form.serializeArray().forEach(function (data) {
      obj[data.name] = data.value;
    });
    return obj;
  }());

  if (jsbin.settings.useCompression) {
    compressData('html,css,javascript', data);
  }

  if (ajax) {
    $.ajax({
      url: $form.attr('action'),
      data: data,
      dataType: 'json',
      type: 'post',
      headers: {'Accept': 'application/json'},
      success: function (data) {
        var $binGroup,
            edit;

        $form.attr('action', data.url + '/save');
        if (ajaxCallback) {
          ajaxCallback(data);
        }

        sessionStorage.setItem('checksum', data.checksum);
        saveChecksum = data.checksum;

        jsbin.state.checksum = saveChecksum;
        jsbin.state.code = data.code;
        jsbin.state.revision = data.revision;
        jsbin.state.metadata = { name: jsbin.user.name };

        // getURL(true) gets the jsbin without the root attached
        // $binGroup = $('#history tr[data-url="' + jsbin.getURL(true) + '"]');
        // edit = data.edit.replace(location.protocol + '//' + window.location.host, '') + window.location.search;
        // $binGroup.find('td.url a span.first').removeClass('first');
        // $binGroup.before('
' + data.code + '/' + data.revision + '/	Just now	' + data.title + '
');

        if (window.history && window.history.pushState) {
          // updateURL(edit);
          window.history.pushState(null, '', jsbin.getURL() + '/edit');
          sessionStorage.setItem('url', jsbin.getURL());
        } else {
          window.location.hash = data.edit;
        }

        $document.trigger('saved');
      },
      error: function (jqXHR) {
        onSaveError(jqXHR, null);
      },
      complete: function () {
        saving.inprogress(false);
      }
    });
  } else {
    $form.submit();
  }
}

/**
 * Returns the similar part of two strings
 * @param  {String} a first string
 * @param  {String} b second string
 * @return {String}   common substring
 */
function sameStart(a, b) {
  if (a == b) return a;

  var tmp = b.slice(0, 1);
  while (a.indexOf(b.slice(0, tmp.length + 1)) === 0) {
    tmp = b.slice(0, tmp.length + 1);
  }

  return tmp;
}

/*

// refresh the window when we popstate, because for now we don't do an xhr to
// inject the panel content...yet.
window.onpopstate = function onpopstate(event) {
  // ignore the first popstate event, because that comes from the browser...
  if (!onpopstate.first) window.location.reload();
  else onpopstate.first = false;
};

onpopstate.first = true;

function updateURL(path) {
  var old = location.pathname,
      back = true,
      same = sameStart(old, path);
      sameAt = same.length;

  if (updateURL.timer) window.cancelAnimationFrame(updateURL.timer);

  var run = function () {
    if (location.pathname !== path) {
      updateURL.timer = window.requestAnimationFrame(run);
    }

    if (location.pathname !== same) {
      if (back) {
        history.replaceState({ path: path }, '', location.pathname.slice(0, -1));
      } else {
        history.replaceState({ path: path }, '', path.slice(0, location.pathname.length + 1));
      }
    } else {
      back = false;
      history.replaceState({ path: path }, '', path.slice(0, sameAt + 2));
    }
  };

  history.pushState({ path: path }, '', location.pathname.slice(0, -1));

  run();
}

*/
<|endoftext|>


<commit_before>define([
'angular',
'lodash',
'../utils/utils'
],
function(
  angular,
  _,
  utils ){

  /*

    SHIFT+CMD+F for full screen mode
    Used for transparent .png animation

  */

  angular.module( 'animpng', [ 'angularFileUpload' ])
  .directive( 'animpng', [
    function(){
      return {
        scope: true,
        template: [

          '
',
          '<audio controls id="myAudio" autoplay></audio>',
          '<input ',
            'type="file" ',
            'webkitdirectory ',
            'mozdirectory ',
            'nv-file-select ',
            'uploader="uploader" />',
          '
'

        ].join(' '),
        link: function( scope, elem ){
          $( elem ).addClass( 'animpng' )

          var fps = 16;
          var keys = []
          var charKeys = []
          window.loopSave = {}

          function show( me ){
            $( me.canvas ).css({
              'top': 0
            })
          }
          function hide( me ){
            $( me.canvas ).css({
              'top': me.height * -1
            })
          }
          function getFrame( n ){
            return Math.round(( n % 1 * fps-1 ) + 1 ) / fps
          }
          function getSeconds( n ){
            return Math.floor( n )
          }
          function getTimeCode( n ){
            return getSeconds( n ) + getFrame( n )
          }
          function register( keyCode, isOn ){
            var cTime = getTimeCode( $audio.currentTime )
            if ( !window.loopSave[ cTime ] ){
              window.loopSave[ cTime ] = []
            }
            window.loopSave[ cTime ].push([ keyCode, isOn ])
          }
          var firstPress = false
          window.addEventListener( 'keydown',
            function( e ){
              if ( !firstPress ){
                _.each( conf.json.startOn, function( id ){
                  var me = getMe({ key: id })
                  show( me )
                })
                firstPress = true
              }

              // play and pause audio with SPACE

              if ( !!$audio && e.keyCode == 32 ){
                ( $audio.paused ) ? $audio.play() : $audio.pause()
              }

              // handle weird keycodes which could fuck-up the EXPERIENCE, man

              if ( e.keyCode == '91' ){
                throw e
              }
              charKeys[ e.keyCode ] = e.key
              keys[ e.keyCode ] = e.keyCode

              // single mode

              var me = getMe( e )
              if ( !!me ){
                show( me )
              }

              window.$audio = $audio

              // store keypresses for looping with aliasing.

              register( e.keyCode, true )
            },
          false )
          window.addEventListener( 'keyup',
            function( e ){
              register( e.keyCode, false )

              // single mode

              keys[ e.keyCode ] = false

              /*
              // group mode
              var keysArray = getNumberArray( keys )
              if ( keysArray.length > 1 ){
                return
              }
              */

              var me = getMe( e )
              if ( !!me ){
                hide( me )
              }
            },
            false
          )
          function getNumberArray( arr ){
            var newArr = new Array()
            for( var i = 0; i < arr.length; i++ ){
              if ( typeof arr[ i ] == "number" ){
                newArr[ newArr.length ] = arr[ i ]
              }
            }
            return newArr
          }

          // showing and hiding layers

          function getMe( e ){
            return _.find( items, function( o ){
              return o.key == e.key.toUpperCase()
            })
          }

          function getMeNum( n ){
            return getMe({ key: charKeys[ n ] })
          }

          function getSibs( e ){
            return _.filter( items, function( o ){
              return o.key[0] == e.key[0] && o.key != e.key
            })
          }

          // loading files

          function onLoadFile( e ){
            var self = this
            var me = _.find( items, function( o ){
              return o.reader === self
            })
            me.src = e.target.result
            var img = new Image()
            img.onload = onLoadImage
            img.src = e.target.result
          }
          function onLoadImage( e ) {
            var me = _.find( items, function( o ){
              return o.src === e.path[0].currentSrc
            })
            me.canvas = document.createElement( 'canvas' )
            me.height = this.height
            $( elem ).append( me.canvas )
            $( me.canvas ).attr({
              width: this.width,
              height: this.height
            })
            if ( me.key == '_bg' ){
              $( me.canvas ).css( 'z-index', 0 )
            }
            else {
              $( me.canvas ).css({ 'z-index': 1, 'top': this.height * -1 })
            }
            me.canvas.getContext('2d').drawImage( this, 0, 0, this.width, this.height )
          }

          var items = []
          var conf = { file: 'config.json', json: {}}
          var $audio = undefined
          scope.uploader.onAfterAddingFile = function( item ){

            // configure file

            if ( item.file.name == conf.file ){
              var reader = new FileReader()
              reader.onload = ( function( e ){
                conf.json = JSON.parse( e.target.result )
              })
              reader.readAsText( item._file )
              return
            }

            // audio file

            if ( item.file.name == 'audio.mp3' ){
              $audio = $( '#myAudio' )
              var reader = new FileReader()
              reader.onload = function( e ){
                $audio.attr( 'src', e.target.result )
                $audio = document.getElementById( 'myAudio' )

                // initial audio config and events

                $audio.onloadstart = function( e ){
                  $audio.pause()
                }
                var lastCode = 0.0
                $audio.ontimeupdate = function( e ){
                  var now = getTimeCode( $audio.currentTime )
                  while ( lastCode <= now ){
                    lastCode += 1 / fps
                    var loop = window.loopSave[ lastCode ]
                    if ( !!loop ){
                      _.each( loop, function( keyCode ){
                        var me = getMeNum( keyCode[0] )
                        if ( !!me ){
                          ( keyCode[1] ) ? show( me ) : hide( me )
                        }
                      })
                    }
                  }

                  // loop check

                  if ( $audio.currentTime == $audio.duration ){
                    lastCode = 0.0
                  }
                }
              }
              reader.readAsDataURL( item._file )
            }

            // remove everything visible but the canvases

            $( 'input', elem ).remove()
            $( 'body .menu').remove()

            // read the file

            var reader = new FileReader()
            reader.onload = onLoadFile
            items.push({
              key: item.file.name.substring( 0, item.file.name.length - 4 ),
              reader: reader
            })
            reader.readAsDataURL( item._file )
          }
        }
      }
    }
  ])
})<commit_msg>fix for bug causing dropped frames on replay<commit_after>define([
'angular',
'lodash',
'../utils/utils'
],
function(
  angular,
  _,
  utils ){

  /*

    SHIFT+CMD+F for full screen mode
    Used for transparent .png animation

  */

  angular.module( 'animpng', [ 'angularFileUpload' ])
  .directive( 'animpng', [
    function(){
      return {
        scope: true,
        template: [

          '
',
          '<audio controls id="myAudio" autoplay></audio>',
          '<input ',
            'type="file" ',
            'webkitdirectory ',
            'mozdirectory ',
            'nv-file-select ',
            'uploader="uploader" />',
          '
'

        ].join(' '),
        link: function( scope, elem ){
          $( elem ).addClass( 'animpng' )

          var fps = 16
          var keys = []
          var charKeys = []
          window.loopSave = {}

          function show( me ){
            $( me.canvas ).css({
              'top': 0
            })
          }
          function hide( me ){
            $( me.canvas ).css({
              'top': me.height * -1
            })
          }
          function getFrame( n ){
            return Math.round(( ( n % 1 ).toFixed( 3 ) * fps-1 ) + 1 ) / fps
          }
          function getSeconds( n ){
            return Math.floor( n )
          }
          function getTimeCode( n ){
            return getSeconds( n ) + getFrame( n )
          }
          function register( keyCode, isOn ){
            var cTime = getTimeCode( $audio.currentTime )
            if ( !window.loopSave[ cTime ] ){
              window.loopSave[ cTime ] = []
            }
            window.loopSave[ cTime ].push([ keyCode, isOn ])
          }
          var firstPress = false
          window.addEventListener( 'keydown',
            function( e ){
              if ( !firstPress ){
                _.each( conf.json.startOn, function( id ){
                  var me = getMe({ key: id })
                  show( me )
                })
                firstPress = true
              }

              // play and pause audio with SPACE

              if ( !!$audio && e.keyCode == 32 ){
                ( $audio.paused ) ? $audio.play() : $audio.pause()
              }

              // handle weird keycodes which could fuck-up the EXPERIENCE, man

              if ( e.keyCode == '91' ){
                throw e
              }
              charKeys[ e.keyCode ] = e.key
              keys[ e.keyCode ] = e.keyCode

              // single mode

              var me = getMe( e )
              if ( !!me ){
                show( me )
              }

              window.$audio = $audio

              // store keypresses for looping with aliasing.

              register( e.keyCode, true )
            },
          false )
          window.addEventListener( 'keyup',
            function( e ){
              register( e.keyCode, false )

              // single mode

              keys[ e.keyCode ] = false

              var me = getMe( e )
              if ( !!me ){
                hide( me )
              }
            },
            false
          )
          function getNumberArray( arr ){
            var newArr = new Array()
            for( var i = 0; i < arr.length; i++ ){
              if ( typeof arr[ i ] == "number" ){
                newArr[ newArr.length ] = arr[ i ]
              }
            }
            return newArr
          }

          // showing and hiding layers

          function getMe( e ){
            return _.find( items, function( o ){
              return o.key == e.key.toUpperCase()
            })
          }

          function getMeNum( n ){
            return getMe({ key: charKeys[ n ] })
          }

          function getSibs( e ){
            return _.filter( items, function( o ){
              return o.key[0] == e.key[0] && o.key != e.key
            })
          }

          // loading files

          function onLoadFile( e ){
            var self = this
            var me = _.find( items, function( o ){
              return o.reader === self
            })
            me.src = e.target.result
            var img = new Image()
            img.onload = onLoadImage
            img.src = e.target.result
          }
          function onLoadImage( e ) {
            var me = _.find( items, function( o ){
              return o.src === e.path[0].currentSrc
            })
            me.canvas = document.createElement( 'canvas' )
            me.height = this.height
            $( elem ).append( me.canvas )
            $( me.canvas ).attr({
              width: this.width,
              height: this.height
            })
            if ( me.key == '_bg' ){
              $( me.canvas ).css( 'z-index', 0 )
            }
            else {
              $( me.canvas ).css({ 'z-index': 1, 'top': this.height * -1 })
            }
            me.canvas.getContext('2d').drawImage( this, 0, 0, this.width, this.height )
          }

          var items = []
          var conf = { file: 'config.json', json: {}}
          var $audio = undefined
          scope.uploader.onAfterAddingFile = function( item ){

            // configure file

            if ( item.file.name == conf.file ){
              var reader = new FileReader()
              reader.onload = ( function( e ){
                conf.json = JSON.parse( e.target.result )
              })
              reader.readAsText( item._file )
              return
            }

            // audio file

            if ( item.file.name == 'audio.mp3' ){
              $audio = $( '#myAudio' )
              var reader = new FileReader()
              reader.onload = function( e ){
                $audio.attr( 'src', e.target.result )
                $audio = document.getElementById( 'myAudio' )

                // initial audio config and events

                $audio.onloadstart = function( e ){
                  $audio.pause()
                }
                var lastCode = 0.0
                $audio.ontimeupdate = function( e ){
                  var now = getTimeCode( $audio.currentTime )
                  while ( lastCode <= now ){
                    lastCode += ( 1 / fps ).toFixed( 4 )
                    var loop = window.loopSave[ lastCode ]
                    if ( !!loop ){
                      _.each( loop, function( keyCode ){
                        var me = getMeNum( keyCode[0] )
                        if ( !!me ){
                          ( keyCode[1] ) ? show( me ) : hide( me )
                        }
                      })
                    }
                  }

                  // loop check

                  if ( $audio.currentTime == $audio.duration ){
                    lastCode = 0.0
                  }
                }
              }
              reader.readAsDataURL( item._file )
            }

            // remove everything visible but the canvases

            $( 'input', elem ).remove()
            $( 'body .menu').remove()

            // read the file

            var reader = new FileReader()
            reader.onload = onLoadFile
            items.push({
              key: item.file.name.substring( 0, item.file.name.length - 4 ),
              reader: reader
            })
            reader.readAsDataURL( item._file )
          }
        }
      }
    }
  ])
})<|endoftext|>


<commit_before>/* global require console process describe it */

var should = require('should')

var _ = require('lodash')
var superagent = require('superagent')
var async = require('async')

var couch_cacher = require('../.').couchCache

var env = process.env;
var cuser = env.COUCHDB_USER ;
var cpass = env.COUCHDB_PASS ;
var chost = env.COUCHDB_HOST || 'localhost';
var cport = env.COUCHDB_PORT || 5984;

var test_db ='test%2fcouch%2fcacher'
var couch = 'http://'+chost+':'+cport+'/'+test_db

var docs = {'docs':[{'_id':'doc1'
                    ,foo:'bar'}
                   ,{'_id':'doc2'
                    ,'baz':'bat'}
                   ]}
var cacher = couch_cacher()
describe('instantiate couchcacher',function(){
    it('should have its four methods',function(done){
        cacher.should.have.property('stash')
        cacher.should.have.property('save')
        cacher.should.have.property('get')
        cacher.should.have.property('reset')
        done()
    })
})

describe('use couchcacher',function(){
    it('should stash docs')
    it('should save docs')
    it('should reset docs')

})

var ts = new Date(2008,6,25,13,0).getTime()/ 1000
var endts =  new Date(2008,6,25,15,0).getTime()/1000

describe('couchCache get',function(){
    it('can get something',function(done){
        var get = cacher.get(function(record){console.log(record)})

        async.parallel([function(cb){
                            var feature = {'properties':{'detector_id':'1013410'
                                                        ,'ts':ts
                                                        ,'endts':endts
                                                        }}

                            get(feature
                                  ,function(e,d){
                                       console.log(d)
                                       cb(e)
                                   });
                        }
                       ,function(cb){
                            var feature = {'properties':{'detector_id':'1010510'
                                                        ,'ts':ts
                                                        ,'endts':endts
                                                        }}
                            get(feature
                                  ,function(e,d){
                                       console.log(d)
                                       cb(e)
                                   })
                        }]
                      ,done)

    })
})
<commit_msg>delete env vars usage in test<commit_after>/* global require console process describe it */

var should = require('should')

var _ = require('lodash')
var superagent = require('superagent')
var async = require('async')

var couch_cacher = require('../.').couchCache

var test_db ='test%2fcouch%2fcacher'
var couch = 'http://'+chost+':'+cport+'/'+test_db

var docs = {'docs':[{'_id':'doc1'
                    ,foo:'bar'}
                   ,{'_id':'doc2'
                    ,'baz':'bat'}
                   ]}
var cacher = couch_cacher()
describe('instantiate couchcacher',function(){
    it('should have its four methods',function(done){
        cacher.should.have.property('stash')
        cacher.should.have.property('save')
        cacher.should.have.property('get')
        cacher.should.have.property('reset')
        done()
    })
})

describe('use couchcacher',function(){
    it('should stash docs')
    it('should save docs')
    it('should reset docs')

})

var ts = new Date(2008,6,25,13,0).getTime()/ 1000
var endts =  new Date(2008,6,25,15,0).getTime()/1000

describe('couchCache get',function(){
    it('can get something',function(done){
        var get = cacher.get(function(record){console.log(record)})

        async.parallel([function(cb){
                            var feature = {'properties':{'detector_id':'1013410'
                                                        ,'ts':ts
                                                        ,'endts':endts
                                                        }}

                            get(feature
                                  ,function(e,d){
                                       console.log(d)
                                       cb(e)
                                   });
                        }
                       ,function(cb){
                            var feature = {'properties':{'detector_id':'1010510'
                                                        ,'ts':ts
                                                        ,'endts':endts
                                                        }}
                            get(feature
                                  ,function(e,d){
                                       console.log(d)
                                       cb(e)
                                   })
                        }]
                      ,done)

    })
})
<|endoftext|>


<commit_before>function Artwork($http) {

  this.getArtworks = function() {
    return $http.get('http://localhost:3000/artworks')
  }

  this.getArtwork = function(id) {
    return $http.get('http://localhost:3000/artworks/' + id + '.json')
  }

}

angular
  .module('app')
  .service('Artwork', Artwork)
<commit_msg>readd destroyArtwork function<commit_after>function Artwork($http) {

  this.getArtworks = function() {
    return $http.get('http://localhost:3000/artworks')
  }

  this.getArtwork = function(id) {
    return $http.get('http://localhost:3000/artworks/' + id + '.json')
  }

  this.destroyArtwork = function(id) {
    return $http.delete('http://localhost:3000/artworks/' + id)
  }
}

angular
  .module('app')
  .service('Artwork', Artwork)
<|endoftext|>