<commit_before>/*jslint devel: true, nomen: true, indent: 2, maxlen: 100 */

"use strict";

var api = require('./lib/api');
var notifier = require('./lib/notifier');
var parser = require('./lib/parser');

var initialized = false;

/**
 *
 * Rollbar:
 *
 *  Handle caught and uncaught exceptions, and report messages back to rollbar.
 *
 *  This library requires an account at http://rollbar.com/.
 *
 * Example usage:
 *
 *  Express -
 *
 *     var express = require('express');
 *     var rollbar = require('rollbar');
 *
 *     var app = express();
 *
 *     app.get('/', function (req, res) {
 *       ...
 *     });
 *
 *     // Use the rollbar error handler to send exceptions to your rollbar account
 *     app.use(rollbar.errorHandler('ROLLBAR_ACCESS_TOKEN'));
 *
 *     app.listen(6943);
 *
 *  Standalone -
 *
 *     var rollbar = require('rollbar');
 *     rollbar.init('ROLLBAR_ACCESS_TOKEN');
 *     rollbar.reportMessage('Hello world', 'debug');
 *
 *  Uncaught exceptions -
 *
 *     var rollbar = require('rollbar');
 *     rollbar.handleUncaughtExceptions('ROLLBAR_ACCESS_TOKEN');
 *
 *  Send exceptions and request data -
 *
 *     app.get('/', function (req, res) {
 *       try {
 *         ...
 *       } catch (e) {
 *         rollbar.handleError(e, req);
 *       }
 *     });
 *
 *  Track people -
 *
 *     app.get('/', function (req, res) {
 *       req.userId = 12345; // or req.user_id
 *       rollbar.reportMessage('Interesting event', req);
 *     });
 */

exports.init = function (accessToken, options) {
  /*
   * Initialize the rollbar library.
   *
   * For more information on each option, see http://rollbar.com/docs/api_items/
   *
   * Supported options, (all optional):
   *
   *  host - Default: os.hostname() - the hostname of the server the node.js process is running on
   *  environment - Default: 'unspecified' - the environment the code is running in. e.g. 'staging'
   *  endpoint - Default: 'https://api.rollbar.com/api/1/' - the url to send items to
   *  root - the path to your code, (not including any trailing slash) which will be used to link
   *    source files on rollbar
   *  branch - the branch in your version control system for this code
   *  codeVersion - the version or revision of your code
   *
   */
  if (!initialized) {
    if (!accessToken && options.enabled !== false) {
      console.error('[Rollbar] Missing access_token.');
      return;
    }

    options = options || {};
    options.environment = options.environment || process.env.NODE_ENV || 'unspecified';

    api.init(accessToken, options);
    notifier.init(api, options);
    initialized = true;
  }
};

/*
 * reportMessage(message, level, request, callback)
 *
 * Sends a message to rollbar with optional level, request and callback.
 * The callback should take a single parameter to indicate if there was an
 * error.
 *
 * Parameters:
 *  message - a string to send to rollbar
 *  level - Default: 'error' - optional level, 'debug', 'info', 'warning', 'error', 'critical'
 *  request - optional request object to send along with the message
 *  callback - optional callback that will be invoked once the message was reported.
 *    callback should take 3 parameters: callback(err, payloadData, response)
 *
 * Examples:
 *
 *  rollbar.reportMessage("User purchased something awesome!", "info");
 *
 *  rollbar.reportMessage("Something suspicious...", "debug", null, function (err, payloadData) {
 *    if (err) {
 *      console.error('Error sending to Rollbar:', err);
 *    } else {
 *      console.log('Reported message to rollbar:');
 *      console.log(payloadData);
 *    }
 *  });
 *
 */
exports.reportMessage = notifier.reportMessage;

/*
 * reportMessageWithPayloadData(message, payloadData, request, callback)
 *
 * The same as reportMessage() but allows you to specify extra data along with the message.
 *
 * Parameters:
 *  message - a string to send to rollbar
 *  payloadData - an object containing key/values to be sent along with the message.
 *    e.g. {level: "warning", fingerprint: "CustomerFingerPrint"}
 *  request - optional request object to send along with the message
 *  callback - optional callback that will be invoked once the message has been sent to Rollbar.
 *    callback should take 3 parameters: callback(err, payloadData, response)
 *
 * Examples:
 *
 *  rollbar.reportMessageWithPayloadData("Memcache miss",
 *    {level: "debug", fingerprint: "Memcache-miss"}, null, function (err) {
 *    // message was queued/sent to rollbar
 *  });
 *
 */
exports.reportMessageWithPayloadData = notifier.reportMessageWithPayloadData;

/*
 * handleError(err, request, callback)
 *
 * Send a details about the error to rollbar along with optional request information.
 *
 * Parameters:
 *  err - an Exception/Error instance
 *  request - an optional request object to send along with the error
 *  callback - optional callback that will be invoked after the error was sent to Rollbar.
 *    callback should take 3 parameters: callback(err, payloadData, response)
 *
 * Examples:
 *
 *  rollbar.handleError(new Error("Could not connect to the database"));
 *
 *  rollbar.handleError(new Error("it's just foobar..."), function (err) {
 *    // error was queued/sent to rollbar
 *  });
 *
 *  rollbar.handleError(new Error("invalid request!"), req);
 *
 */
exports.handleError = notifier.handleError;

/*
 * handleErrorWithPayloadData(err, payloadData, request, callback)
 *
 * The same as handleError() but allows you to specify additional data to log along with the error,
 * as well as other payload options.
 *
 * Parameters:
 *  err - an Exception/Error instance
 *  payloadData - an object containing keys/values to be sent along with the error report.
 *    e.g. {level: "warning"}
 *  request - optional request object to send along with the message
 *  callback - optional callback that will be invoked after the error was sent to Rollbar.
 *    callback should take 3 parameters: callback(err, payloadData, response)
 *
 *  Examples:
 *
 *   rollbar.handleError(new Error("Could not connect to database"), {level: "warning"});
 *   rollbar.handleError(new Error("Could not connect to database"),
 *    {custom: {someKey: "its value, otherKey: ["other", "value"]}});
 *   rollbar.handleError(new Error("error message"), {}, req, function (err) {
 *     // error was queued/sent to rollbar
 *   });
 */
exports.handleErrorWithPayloadData = notifier.handleErrorWithPayloadData;

exports.errorHandler = function (accessToken, options) {
  /*
   * A middleware handler for connect and express.js apps. For a list
   * of supported options, see the init() docs above.
   *
   * All exceptions thrown from inside an express or connect get/post/etc... handler
   * will be sent to rollbar when this middleware is installed.
   */
  exports.init(accessToken, options);
  return function (err, req, res, next) {
    var cb = function (rollbarErr) {
      if (rollbarErr) {
        console.error('[Rollbar] Error reporting to rollbar, ignoring: ' + rollbarErr);
      }
      return next(err, req, res);
    };

    if (!err) {
      return next(err, req, res);
    }

    if (err instanceof Error) {
      return notifier.handleError(err, req, cb);
    }

    return notifier.reportMessage('Error: ' + err, 'error', req, cb);
  };
};

exports.handleUncaughtExceptions = function (accessToken, options) {
  /*
   * Registers a handler for the process.uncaughtException event.
   *
   * If options.exitOnUncaughtException is set to true, the notifier will
   * immediately send the uncaught exception + all queued items to rollbar,
   * then call process.exit(1).
   *
   * Note: The node.js authors advise against using these type of handlers.
   * More info: http://nodejs.org/api/process.html#process_event_uncaughtexception
   *
   */

  // Default to not exiting on uncaught exceptions unless options.exitOnUncaughtException is set.
  options = options || {};
  var exitOnUncaught = options.exitOnUncaughtException === undefined ?
        false : !!options.exitOnUncaughtException;
  delete options.exitOnUncaughtException;

  exports.init(accessToken, options);

  if (initialized) {
    process.on('uncaughtException', function (err) {
      console.error('[Rollbar] Handling uncaught exception.');
      console.error(err);

      notifier.handleError(err, function (err) {
        if (err) {
          console.error('[Rollbar] Encountered an error while handling an uncaught exception.');
          console.error(err);
        }

        if (exitOnUncaught) {
          process.exit(1);
        }
      });
    });
  } else {
    console.error('[Rollbar] Rollbar is not initialized. Uncaught exceptions will not be tracked.');
  }
};

exports.api = api;
exports.notifier = notifier;
<commit_msg>Don't crash if rollbar.init() is called without options<commit_after>/*jslint devel: true, nomen: true, indent: 2, maxlen: 100 */

"use strict";

var api = require('./lib/api');
var notifier = require('./lib/notifier');
var parser = require('./lib/parser');

var initialized = false;

/**
 *
 * Rollbar:
 *
 *  Handle caught and uncaught exceptions, and report messages back to rollbar.
 *
 *  This library requires an account at http://rollbar.com/.
 *
 * Example usage:
 *
 *  Express -
 *
 *     var express = require('express');
 *     var rollbar = require('rollbar');
 *
 *     var app = express();
 *
 *     app.get('/', function (req, res) {
 *       ...
 *     });
 *
 *     // Use the rollbar error handler to send exceptions to your rollbar account
 *     app.use(rollbar.errorHandler('ROLLBAR_ACCESS_TOKEN'));
 *
 *     app.listen(6943);
 *
 *  Standalone -
 *
 *     var rollbar = require('rollbar');
 *     rollbar.init('ROLLBAR_ACCESS_TOKEN');
 *     rollbar.reportMessage('Hello world', 'debug');
 *
 *  Uncaught exceptions -
 *
 *     var rollbar = require('rollbar');
 *     rollbar.handleUncaughtExceptions('ROLLBAR_ACCESS_TOKEN');
 *
 *  Send exceptions and request data -
 *
 *     app.get('/', function (req, res) {
 *       try {
 *         ...
 *       } catch (e) {
 *         rollbar.handleError(e, req);
 *       }
 *     });
 *
 *  Track people -
 *
 *     app.get('/', function (req, res) {
 *       req.userId = 12345; // or req.user_id
 *       rollbar.reportMessage('Interesting event', req);
 *     });
 */

exports.init = function (accessToken, options) {
  /*
   * Initialize the rollbar library.
   *
   * For more information on each option, see http://rollbar.com/docs/api_items/
   *
   * Supported options, (all optional):
   *
   *  host - Default: os.hostname() - the hostname of the server the node.js process is running on
   *  environment - Default: 'unspecified' - the environment the code is running in. e.g. 'staging'
   *  endpoint - Default: 'https://api.rollbar.com/api/1/' - the url to send items to
   *  root - the path to your code, (not including any trailing slash) which will be used to link
   *    source files on rollbar
   *  branch - the branch in your version control system for this code
   *  codeVersion - the version or revision of your code
   *
   */
  if (!initialized) {
    options = options || {};
    if (!accessToken && options.enabled !== false) {
      console.error('[Rollbar] Missing access_token.');
      return;
    }

    options.environment = options.environment || process.env.NODE_ENV || 'unspecified';

    api.init(accessToken, options);
    notifier.init(api, options);
    initialized = true;
  }
};

/*
 * reportMessage(message, level, request, callback)
 *
 * Sends a message to rollbar with optional level, request and callback.
 * The callback should take a single parameter to indicate if there was an
 * error.
 *
 * Parameters:
 *  message - a string to send to rollbar
 *  level - Default: 'error' - optional level, 'debug', 'info', 'warning', 'error', 'critical'
 *  request - optional request object to send along with the message
 *  callback - optional callback that will be invoked once the message was reported.
 *    callback should take 3 parameters: callback(err, payloadData, response)
 *
 * Examples:
 *
 *  rollbar.reportMessage("User purchased something awesome!", "info");
 *
 *  rollbar.reportMessage("Something suspicious...", "debug", null, function (err, payloadData) {
 *    if (err) {
 *      console.error('Error sending to Rollbar:', err);
 *    } else {
 *      console.log('Reported message to rollbar:');
 *      console.log(payloadData);
 *    }
 *  });
 *
 */
exports.reportMessage = notifier.reportMessage;

/*
 * reportMessageWithPayloadData(message, payloadData, request, callback)
 *
 * The same as reportMessage() but allows you to specify extra data along with the message.
 *
 * Parameters:
 *  message - a string to send to rollbar
 *  payloadData - an object containing key/values to be sent along with the message.
 *    e.g. {level: "warning", fingerprint: "CustomerFingerPrint"}
 *  request - optional request object to send along with the message
 *  callback - optional callback that will be invoked once the message has been sent to Rollbar.
 *    callback should take 3 parameters: callback(err, payloadData, response)
 *
 * Examples:
 *
 *  rollbar.reportMessageWithPayloadData("Memcache miss",
 *    {level: "debug", fingerprint: "Memcache-miss"}, null, function (err) {
 *    // message was queued/sent to rollbar
 *  });
 *
 */
exports.reportMessageWithPayloadData = notifier.reportMessageWithPayloadData;

/*
 * handleError(err, request, callback)
 *
 * Send a details about the error to rollbar along with optional request information.
 *
 * Parameters:
 *  err - an Exception/Error instance
 *  request - an optional request object to send along with the error
 *  callback - optional callback that will be invoked after the error was sent to Rollbar.
 *    callback should take 3 parameters: callback(err, payloadData, response)
 *
 * Examples:
 *
 *  rollbar.handleError(new Error("Could not connect to the database"));
 *
 *  rollbar.handleError(new Error("it's just foobar..."), function (err) {
 *    // error was queued/sent to rollbar
 *  });
 *
 *  rollbar.handleError(new Error("invalid request!"), req);
 *
 */
exports.handleError = notifier.handleError;

/*
 * handleErrorWithPayloadData(err, payloadData, request, callback)
 *
 * The same as handleError() but allows you to specify additional data to log along with the error,
 * as well as other payload options.
 *
 * Parameters:
 *  err - an Exception/Error instance
 *  payloadData - an object containing keys/values to be sent along with the error report.
 *    e.g. {level: "warning"}
 *  request - optional request object to send along with the message
 *  callback - optional callback that will be invoked after the error was sent to Rollbar.
 *    callback should take 3 parameters: callback(err, payloadData, response)
 *
 *  Examples:
 *
 *   rollbar.handleError(new Error("Could not connect to database"), {level: "warning"});
 *   rollbar.handleError(new Error("Could not connect to database"),
 *    {custom: {someKey: "its value, otherKey: ["other", "value"]}});
 *   rollbar.handleError(new Error("error message"), {}, req, function (err) {
 *     // error was queued/sent to rollbar
 *   });
 */
exports.handleErrorWithPayloadData = notifier.handleErrorWithPayloadData;

exports.errorHandler = function (accessToken, options) {
  /*
   * A middleware handler for connect and express.js apps. For a list
   * of supported options, see the init() docs above.
   *
   * All exceptions thrown from inside an express or connect get/post/etc... handler
   * will be sent to rollbar when this middleware is installed.
   */
  exports.init(accessToken, options);
  return function (err, req, res, next) {
    var cb = function (rollbarErr) {
      if (rollbarErr) {
        console.error('[Rollbar] Error reporting to rollbar, ignoring: ' + rollbarErr);
      }
      return next(err, req, res);
    };

    if (!err) {
      return next(err, req, res);
    }

    if (err instanceof Error) {
      return notifier.handleError(err, req, cb);
    }

    return notifier.reportMessage('Error: ' + err, 'error', req, cb);
  };
};

exports.handleUncaughtExceptions = function (accessToken, options) {
  /*
   * Registers a handler for the process.uncaughtException event.
   *
   * If options.exitOnUncaughtException is set to true, the notifier will
   * immediately send the uncaught exception + all queued items to rollbar,
   * then call process.exit(1).
   *
   * Note: The node.js authors advise against using these type of handlers.
   * More info: http://nodejs.org/api/process.html#process_event_uncaughtexception
   *
   */

  // Default to not exiting on uncaught exceptions unless options.exitOnUncaughtException is set.
  options = options || {};
  var exitOnUncaught = options.exitOnUncaughtException === undefined ?
        false : !!options.exitOnUncaughtException;
  delete options.exitOnUncaughtException;

  exports.init(accessToken, options);

  if (initialized) {
    process.on('uncaughtException', function (err) {
      console.error('[Rollbar] Handling uncaught exception.');
      console.error(err);

      notifier.handleError(err, function (err) {
        if (err) {
          console.error('[Rollbar] Encountered an error while handling an uncaught exception.');
          console.error(err);
        }

        if (exitOnUncaught) {
          process.exit(1);
        }
      });
    });
  } else {
    console.error('[Rollbar] Rollbar is not initialized. Uncaught exceptions will not be tracked.');
  }
};

exports.api = api;
exports.notifier = notifier;
<|endoftext|>


<commit_before>// InstantClick 1.1
// (C) 2014 Alexandre Dieulot
// http://instantclick.io/license.html

var InstantClick = function(document, location) {
	var currentLocationWithoutHash
	var pHistory = {} // short for "preloadHistory"
	var p = {} // short for "preloads"
	var supported = 'pushState' in history
	var useBlacklist
	var listeners = {change: []}

	function removeHash(url) {
		var index = url.indexOf('#')
		if (index == -1) {
			return url
		}
		return url.substr(0, index)
	}

	function getLinkTarget(target) {
		while (target.nodeName != 'A') {
			target = target.parentNode
		}
		return target
	}

	function on(type, listener) {
		listeners[type].push(listener)
	}

	function triggerPageEvent(type) {
		for (var i = 0; i < listeners[type].length; i++) {
			listeners[type][i]()
		}
	}

	function applyBody(body) {
		var doc = document.implementation.createHTMLDocument('')
		doc.documentElement.innerHTML = body
		document.body = doc.body
	}

	function debug() {
		return {
			currentLocationWithoutHash: currentLocationWithoutHash,
			p: p,
			pHistory: pHistory,
			supported: supported,
			useBlacklist: useBlacklist
		}
	}

	function instantanize(isInitializing) {
		var as = document.getElementsByTagName('a'), a, domain = location.protocol + '//' + location.host
		for (var i = as.length - 1; i >= 0; i--) {
			a = as[i]
			if (a.target || // target="_blank" etc.
				a.href.indexOf(domain + '/') != 0 || // another domain (or no href attribute)
				a.href.indexOf('#') > -1 && removeHash(a.href) == currentLocationWithoutHash || // link to an anchor
				(useBlacklist ? a.hasAttribute('data-no-instant') : !a.hasAttribute('data-instant'))) {
				continue
			}
			a.addEventListener('mouseover', queue)
			a.addEventListener('click', click)
		}
		if (!isInitializing) {
			var scripts = document.getElementsByTagName('script'), script, copy, parentNode, nextSibling
			for (i = 0, j = scripts.length; i < j; i++) {
				script = scripts[i]
				if (script.hasAttribute('data-no-instant')) {
					continue
				}
				copy = document.createElement('script')
				if (script.src) {
					copy.src = script.src
				}
				if (script.innerHTML) {
					copy.innerHTML = script.innerHTML
				}
				parentNode = script.parentNode
				nextSibling = script.nextSibling
				parentNode.removeChild(script)
				parentNode.insertBefore(copy, nextSibling)
			}
		}
		triggerPageEvent('change')
	}

	function queue(e) {
		var a = getLinkTarget(e.target)
		a.addEventListener('mouseout', mouseout)
		preload(a.href)
	}

	function preload(url) {
		if ((p.isPreloading && url == p.url) || (p.isPreloading && p.isWaitingForCompletion)) {
			return
		}
		p.isPreloading = true
		p.isWaitingForCompletion = false
		p.url = url
		p.body = false
		p.hasBody = true
		p.timingStart = +new Date
		p.timing = false
		p.xhr.open('GET', url)
		p.xhr.send()
	}

	function readystatechange(e) {
		if (p.xhr.readyState < 4) {
			return
		}

		var text = p.xhr.responseText

		p.timing = +new Date - p.timingStart
		// Note: For debugging (`InstantClick.debug`), we know the page
		// has been preloaded if `p.timing` isn't false.

		var titleIndex = text.indexOf('<title')
		if (titleIndex > -1) {
			p.title = text.substr(text.indexOf('>', titleIndex) + 1)
			p.title = p.title.substr(0, p.title.indexOf('</title'))
		}

		var bodyIndex = text.indexOf('<body')
		if (bodyIndex > -1) {
			p.body = text.substr(bodyIndex)
			var closingIndex = p.body.indexOf('</body')
			if (closingIndex > -1) {
				p.body = p.body.substr(0, closingIndex)
			}

			var urlWithoutHash = removeHash(p.url)
			pHistory[urlWithoutHash] = {
				body: p.body,
				title: p.title,
				scrollY: urlWithoutHash in pHistory ? pHistory[urlWithoutHash].scrollY : 0
			}
		}
		else {
			p.hasBody = false
		}

		if (p.isWaitingForCompletion) {
			display(p.url)
		}
	}

	function click(e) {
		if (e.which > 1 || e.metaKey || e.ctrlKey) { // Opening in new tab
			return
		}
		e.preventDefault()
		display(getLinkTarget(e.target).href)
	}

	function display(url) {
		if (!p.isPreloading || (p.isPreloading && p.isWaitingForCompletion)) {
			/* If the page isn't preloaded, it means
			   the user has focused on a link (with his Tab
			   key) and then pressed Return, which triggered a click.
			   Because very few people do this, it isn't worth handling this
			   case and preloading on focus (also, focussing on a link
			   doesn't mean it's likely that you'll "click" on it), so we just
			   redirect them when they "click".

			   If the page is waiting for completion, the user clicked twice
			   while the page was preloading.
			   Two possibilities:
			   1) He clicks on the same link again, either because it's slow
			      to load (there's no browser loading indicator with
			      InstantClick, so he might think his click hasn't registered
			      if the page isn't loading fast enough) or because he has
			      a habit of double clicking on the web;
			   2) He clicks on another link.

			   In the first case, we redirect him (send him to the page the old
			   way) so that he can have the browser's loading indicator back.
			   In the second case, we redirect him because we haven't preloaded
			   that link, since we were already preloading the last one.

			   Determining if it's a double click might be overkill as there is
			   (hopefully) not that many people that double click on the web.
			   Fighting against the perception that the page is stuck is
			   interesting though, a seemingly good way to do that would be to
			   later incorporate a progress bar.
			*/

			location.href = url
			return
		}
		if (!p.hasBody) {
			location.href = p.url
			return
		}
		if (!p.body) {
			p.isWaitingForCompletion = true
			return
		}
		pHistory[currentLocationWithoutHash].scrollY = scrollY
		p.isPreloading = false
		p.isWaitingForCompletion = false
		applyBody(p.body)
		document.title = p.title
		var hashIndex = p.url.indexOf('#')
		var hashElem = hashIndex > -1 && document.getElementById(p.url.substr(hashIndex + 1))
		var offset = 0
		if (p.url != location.href && hashElem) {
			for (; hashElem.offsetParent; hashElem = hashElem.offsetParent) {
				offset += hashElem.offsetTop
			}
		}
		scrollTo(0, offset)
		history.pushState(null, null, p.url)
		currentLocationWithoutHash = removeHash(location.href)
		instantanize()
	}

	function mouseout() {
		if (!p.isPreloading || (p.isPreloading && p.isWaitingForCompletion)) {
			return
		}
		p.xhr.abort()
		p.isPreloading = false
		p.isWaitingForCompletion = false
	}

	function init(arg_useBlacklist) {
		if (!supported) {
			triggerPageEvent('change')
			return
		}
		if (currentLocationWithoutHash) { // Already initialized
			return
		}
		useBlacklist = !!arg_useBlacklist
		currentLocationWithoutHash = removeHash(location.href)
		pHistory[currentLocationWithoutHash] = {
			body: document.body.outerHTML,
			title: document.title,
			scrollY: scrollY
		}
		p.xhr = new XMLHttpRequest()
		p.xhr.addEventListener('readystatechange', readystatechange)
		p.url = false
		p.body = false
		p.hasBody = true
		p.title = false
		p.isPreloading = false
		p.isWaitingForCompletion = false
		p.timingStart = false
		p.timing = false
		instantanize(true)

		addEventListener('popstate', function() {
			var loc = removeHash(location.href)
			if (loc == currentLocationWithoutHash) {
				return
			}
			if (!(loc in pHistory)) {
				location.href = location.href // Reloads the page and makes use of cache for assets, unlike location.reload()
				return
			}
			pHistory[currentLocationWithoutHash].scrollY = scrollY

			currentLocationWithoutHash = loc
			applyBody(pHistory[loc].body)
			scrollTo(0, pHistory[loc].scrollY)
			document.title = pHistory[loc].title
			instantanize()
		})
	}

	return {
		init: init,
		supported: supported,
		on: on,
		debug: debug
	}
}(document, location);
<commit_msg>Bugfix (regression): No redirect if displaying a bit later<commit_after>// InstantClick 1.1
// (C) 2014 Alexandre Dieulot
// http://instantclick.io/license.html

var InstantClick = function(document, location) {
	var currentLocationWithoutHash
	var pHistory = {} // short for "preloadHistory"
	var p = {} // short for "preloads"
	var supported = 'pushState' in history
	var useBlacklist
	var listeners = {change: []}

	function removeHash(url) {
		var index = url.indexOf('#')
		if (index == -1) {
			return url
		}
		return url.substr(0, index)
	}

	function getLinkTarget(target) {
		while (target.nodeName != 'A') {
			target = target.parentNode
		}
		return target
	}

	function on(type, listener) {
		listeners[type].push(listener)
	}

	function triggerPageEvent(type) {
		for (var i = 0; i < listeners[type].length; i++) {
			listeners[type][i]()
		}
	}

	function applyBody(body) {
		var doc = document.implementation.createHTMLDocument('')
		doc.documentElement.innerHTML = body
		document.body = doc.body
	}

	function debug() {
		return {
			currentLocationWithoutHash: currentLocationWithoutHash,
			p: p,
			pHistory: pHistory,
			supported: supported,
			useBlacklist: useBlacklist
		}
	}

	function instantanize(isInitializing) {
		var as = document.getElementsByTagName('a'), a, domain = location.protocol + '//' + location.host
		for (var i = as.length - 1; i >= 0; i--) {
			a = as[i]
			if (a.target || // target="_blank" etc.
				a.href.indexOf(domain + '/') != 0 || // another domain (or no href attribute)
				a.href.indexOf('#') > -1 && removeHash(a.href) == currentLocationWithoutHash || // link to an anchor
				(useBlacklist ? a.hasAttribute('data-no-instant') : !a.hasAttribute('data-instant'))) {
				continue
			}
			a.addEventListener('mouseover', queue)
			a.addEventListener('click', click)
		}
		if (!isInitializing) {
			var scripts = document.getElementsByTagName('script'), script, copy, parentNode, nextSibling
			for (i = 0, j = scripts.length; i < j; i++) {
				script = scripts[i]
				if (script.hasAttribute('data-no-instant')) {
					continue
				}
				copy = document.createElement('script')
				if (script.src) {
					copy.src = script.src
				}
				if (script.innerHTML) {
					copy.innerHTML = script.innerHTML
				}
				parentNode = script.parentNode
				nextSibling = script.nextSibling
				parentNode.removeChild(script)
				parentNode.insertBefore(copy, nextSibling)
			}
		}
		triggerPageEvent('change')
	}

	function queue(e) {
		var a = getLinkTarget(e.target)
		a.addEventListener('mouseout', mouseout)
		preload(a.href)
	}

	function preload(url) {
		if ((p.isPreloading && url == p.url) || (p.isPreloading && p.isWaitingForCompletion)) {
			return
		}
		p.isPreloading = true
		p.isWaitingForCompletion = false
		p.url = url
		p.body = false
		p.hasBody = true
		p.timingStart = +new Date
		p.timing = false
		p.xhr.open('GET', url)
		p.xhr.send()
	}

	function readystatechange(e) {
		if (p.xhr.readyState < 4) {
			return
		}

		var text = p.xhr.responseText

		p.timing = +new Date - p.timingStart
		// Note: For debugging (`InstantClick.debug`), we know the page
		// has been preloaded if `p.timing` isn't false.

		var titleIndex = text.indexOf('<title')
		if (titleIndex > -1) {
			p.title = text.substr(text.indexOf('>', titleIndex) + 1)
			p.title = p.title.substr(0, p.title.indexOf('</title'))
		}

		var bodyIndex = text.indexOf('<body')
		if (bodyIndex > -1) {
			p.body = text.substr(bodyIndex)
			var closingIndex = p.body.indexOf('</body')
			if (closingIndex > -1) {
				p.body = p.body.substr(0, closingIndex)
			}

			var urlWithoutHash = removeHash(p.url)
			pHistory[urlWithoutHash] = {
				body: p.body,
				title: p.title,
				scrollY: urlWithoutHash in pHistory ? pHistory[urlWithoutHash].scrollY : 0
			}
		}
		else {
			p.hasBody = false
		}

		if (p.isWaitingForCompletion) {
			p.isWaitingForCompletion = false
			display(p.url)
		}
	}

	function click(e) {
		if (e.which > 1 || e.metaKey || e.ctrlKey) { // Opening in new tab
			return
		}
		e.preventDefault()
		display(getLinkTarget(e.target).href)
	}

	function display(url) {
		if (!p.isPreloading || (p.isPreloading && p.isWaitingForCompletion)) {
			/* If the page isn't preloaded, it means
			   the user has focused on a link (with his Tab
			   key) and then pressed Return, which triggered a click.
			   Because very few people do this, it isn't worth handling this
			   case and preloading on focus (also, focussing on a link
			   doesn't mean it's likely that you'll "click" on it), so we just
			   redirect them when they "click".

			   If the page is waiting for completion, the user clicked twice
			   while the page was preloading.
			   Two possibilities:
			   1) He clicks on the same link again, either because it's slow
			      to load (there's no browser loading indicator with
			      InstantClick, so he might think his click hasn't registered
			      if the page isn't loading fast enough) or because he has
			      a habit of double clicking on the web;
			   2) He clicks on another link.

			   In the first case, we redirect him (send him to the page the old
			   way) so that he can have the browser's loading indicator back.
			   In the second case, we redirect him because we haven't preloaded
			   that link, since we were already preloading the last one.

			   Determining if it's a double click might be overkill as there is
			   (hopefully) not that many people that double click on the web.
			   Fighting against the perception that the page is stuck is
			   interesting though, a seemingly good way to do that would be to
			   later incorporate a progress bar.
			*/

			location.href = url
			return
		}
		if (!p.hasBody) {
			location.href = p.url
			return
		}
		if (!p.body) {
			p.isWaitingForCompletion = true
			return
		}
		pHistory[currentLocationWithoutHash].scrollY = scrollY
		p.isPreloading = false
		p.isWaitingForCompletion = false
		applyBody(p.body)
		document.title = p.title
		var hashIndex = p.url.indexOf('#')
		var hashElem = hashIndex > -1 && document.getElementById(p.url.substr(hashIndex + 1))
		var offset = 0
		if (p.url != location.href && hashElem) {
			for (; hashElem.offsetParent; hashElem = hashElem.offsetParent) {
				offset += hashElem.offsetTop
			}
		}
		scrollTo(0, offset)
		history.pushState(null, null, p.url)
		currentLocationWithoutHash = removeHash(location.href)
		instantanize()
	}

	function mouseout() {
		if (!p.isPreloading || (p.isPreloading && p.isWaitingForCompletion)) {
			return
		}
		p.xhr.abort()
		p.isPreloading = false
		p.isWaitingForCompletion = false
	}

	function init(arg_useBlacklist) {
		if (!supported) {
			triggerPageEvent('change')
			return
		}
		if (currentLocationWithoutHash) { // Already initialized
			return
		}
		useBlacklist = !!arg_useBlacklist
		currentLocationWithoutHash = removeHash(location.href)
		pHistory[currentLocationWithoutHash] = {
			body: document.body.outerHTML,
			title: document.title,
			scrollY: scrollY
		}
		p.xhr = new XMLHttpRequest()
		p.xhr.addEventListener('readystatechange', readystatechange)
		p.url = false
		p.body = false
		p.hasBody = true
		p.title = false
		p.isPreloading = false
		p.isWaitingForCompletion = false
		p.timingStart = false
		p.timing = false
		instantanize(true)

		addEventListener('popstate', function() {
			var loc = removeHash(location.href)
			if (loc == currentLocationWithoutHash) {
				return
			}
			if (!(loc in pHistory)) {
				location.href = location.href // Reloads the page and makes use of cache for assets, unlike location.reload()
				return
			}
			pHistory[currentLocationWithoutHash].scrollY = scrollY

			currentLocationWithoutHash = loc
			applyBody(pHistory[loc].body)
			scrollTo(0, pHistory[loc].scrollY)
			document.title = pHistory[loc].title
			instantanize()
		})
	}

	return {
		init: init,
		supported: supported,
		on: on,
		debug: debug
	}
}(document, location);
<|endoftext|>


<commit_before>var beep = require('interface/beep.js');
var _ = require('underscore');
var oled = null;
var currentProgram = null;
var backupProgram = null;
var currentName = "";
var stack = [];
var screenSaverHandle = null;

var beepEnter = beep.sine(2954, 0.04);
var beepBack = beep.sine(1447, 0.04);
var beepClick = beep.sine(738, 0.02);
var beepAlarm = beep.sine2((2954, 1540, 0.5);

exports.busy = false;

function activity() {
    oled.activity();
}

exports.init = function(menuController) {
    oled = menuController;
}

function load(program, selected) {
    if(!program) return;// exports.back();
    if (program.alternate && typeof program.alternate == "function") {
        var alternate = program.alternate();
        if (alternate !== false) {
            load(alternate, selected);
            return;
        }
    }

    currentName = program.name;
    currentProgram = _.clone(program);

    if (currentProgram.items && currentProgram.items.slice) {
        currentProgram.items = currentProgram.items.slice(0);
        currentProgram.items = currentProgram.items.filter(function(item) {
            if (typeof item.condition === "function") {
                return item.condition();
            } else {
                return true;
            }
        });
        for (var i = 0; i < currentProgram.items.length; i++) {
            var item = currentProgram.items[i];
            if (typeof item.name == 'function') {
                currentProgram.items[i] = _.clone(item);
                item = currentProgram.items[i];
                item.name = item.name();
            }
            if (selected == null) {
                if (item.selected && typeof item.selected === "function") {
                    if (item.selected()) {
                        selected = i;
                        break;
                    }
                } else if (item.action && item.action.selected && typeof item.action.selected === "function") {
                    if (item.action.selected()) {
                        selected = i;
                        break;
                    }
                }
            }
        }
        if(selected >= currentProgram.items.length) selected = currentProgram.items.length - 1;
    }
    if (currentProgram.type == "timelapse") {
        oled.setTimelapseMode(true);
    } else {
        oled.setTimelapseMode(false);
    }
    if (currentProgram.type == "menu" && currentProgram.hasImages == true) {
        oled.createMenuImage(currentProgram.items.map(function(item) {
            return {
                name: item.name,
                line2: item.line2,
                image: item.image
            };
        }), selected || 0);
        oled.update();
    } else if (currentProgram.type == "menu") {
        oled.create(currentProgram.items.map(function(item) {
            return item.name;
        }), selected || 0);
        oled.update();
    }
    if (currentProgram.type == "options") {
        oled.value(currentProgram.items, selected || 0);
        oled.update();
    }
    if (currentProgram.type == "textInput") {
        oled.text(currentProgram.name, currentProgram.value);
        oled.update();
    }
    if (currentProgram.type == "textDisplay") {
        oled.displayText(currentProgram.name, currentProgram.value);
        oled.update();
    }
    if (currentProgram.type == "png" && currentProgram.file) {
        oled.png(currentProgram.file);
    }
    if (currentProgram.type == "function" && currentProgram.fn) {
        currentProgram.fn(currentProgram.arg, function(err, program) {
            if(!err && program && (program.type)) {
                load(program);
            } else {
                console.log("function completed, going back");
                setTimeout(back);
            }
        });
    }
    if (currentProgram.type == "menuFunction" && currentProgram.fn) {
        currentProgram.fn(currentProgram.arg, function(err, p) {
            if (!err) exports.load(p, true);
        });
    }
}

exports.load = function(menuProgram, noPush, selected, forceStack) {
    exports.busy = false;
    if(currentProgram && currentProgram.intervalHandle) {
        clearInterval(currentProgram.intervalHandle)
        currentProgram.intervalHandle = null;
    }
    if ((forceStack && backupProgram != null) || (backupProgram != null && !noPush && backupProgram.type != "options" && backupProgram.type != "function")) {
        stack.push({
            program: backupProgram,
            selected: oled.selected,
            name: currentName
        });
    }
    backupProgram = menuProgram;

    if (typeof menuProgram == 'function') {
        exports.busy = true;
        oled.showBusy();
        menuProgram(function(arg, program) {
            exports.busy = false;
            load(program, selected);
        });
    } else {
        load(menuProgram, selected);
    }

}

exports.reload = function() {
    if (backupProgram.type == "menu" || backupProgram.type == "options") {
        exports.load(backupProgram, true, oled.selected);
    }
}

exports.up = function(alt) {
    activity();
    if(exports.busy) return;
    if (currentProgram.type == "menu" || currentProgram.type == "options") {
        oled.up();
    } else if(currentProgram.type == "textDisplay") {
        oled.up();
    } else if(currentProgram.type == "textInput") {
        if(alt) {
            oled.textMoveBackward();
        } else {
            oled.up();
        }
    }
}
exports.down = function(alt) {
    activity();
    if(exports.busy) return;
    if (currentProgram.type == "menu" || currentProgram.type == "options") {
        oled.down();
    } else if(currentProgram.type == "textDisplay") {
        oled.down();
    } else if(currentProgram.type == "textInput") {
        if(alt) {
            oled.textMoveForward();
        } else {
            oled.down();
        }
    }
}
exports.enter = function(alt) {
    activity();
    if(exports.busy) return;
    beep.play(beepEnter);
    if (currentProgram.type == "menu" || currentProgram.type == "options") {
        if(currentProgram.items[oled.selected]) exports.load(currentProgram.items[oled.selected].action);
    } else if (currentProgram.type == "textInput") {
        if(alt) {
            oled.textMoveForward();
        } else {
            //console.log("resulting string", oled.getTextValue());
            if(currentProgram.onSave) currentProgram.onSave(oled.getTextValue());
            back();
        }
    } else if (currentProgram.type == "png") {
        back();
    } else if (currentProgram.enter && typeof currentProgram.enter == "function") {
        currentProgram.enter();
    }
}
exports.help = function() {
    activity();
    if(exports.busy) return;
    beep.play(beepClick);
    if(currentProgram.type == "textDisplay" && currentProgram.origin == "help") {
        back();
    } else if (currentProgram.type == "menu" || currentProgram.type == "options") {
        if (currentProgram.items[oled.selected] && currentProgram.items[oled.selected].help) {
            exports.load({
                type: "textDisplay",
                origin: "help",
                name: "HELP - " + currentProgram.items[oled.selected].name || currentProgram.name,
                value: currentProgram.items[oled.selected].help
            });
        }
    } else if(currentProgram.help) {
        exports.load({
            type: "textDisplay",
            origin: "help",
            name: "HELP - " + currentProgram.name,
            value: currentProgram.help
        });
    }
}
exports.alert = function(title, text, updateInterval) {
    activity();
    var f, intervalHandle = null;
    if(typeof text === 'function') {
        f = text;
        text = f();
        intervalHandle = setInterval(function() {
            oled.updateDisplayText(f());
        }, updateInterval||1000);
    }
    exports.load({
        type: "textDisplay",
        origin: "alert",
        name: title,
        value: text,
        intervalHandle: intervalHandle
    });
}
exports.currentOrigin = function() {
    return currentProgram.origin;
}
exports.dismissAlert = function() {
    activity();
    if (currentProgram.type == "textDisplay" && currentProgram.origin == "alert" ) back();
}
exports.button3 = function() {
    activity();
    if(exports.busy) return;
    beep.play(beepClick);
    if (currentProgram.type == "menu" && currentProgram.items[oled.selected] && currentProgram.items[oled.selected].button3) {
        currentProgram.items[oled.selected].button3(currentProgram.items[oled.selected]);
    } else if (currentProgram.type == "textInput") {
        oled.textCycleMode();
    } else if (currentProgram.button3 && typeof currentProgram.button3 == "function") {
        currentProgram.button3();
    }
}
function back() {
    if(stack.length == 0) {
        console.log("already at menu top");
        return;
    }
    var b;
    do {
        b = stack.pop();
    } while((!b || !b.name) && stack.length > 0);
    if(currentProgram.origin == "prompt" && stack.length > 0) {
        do {
            b = stack.pop();
        } while((!b || !b.name) && stack.length > 0);
    }
    console.log("BACK to " + b.name);
    exports.load(b.program, true, b.selected);
}
exports.back = function() {
    exports.busy = false;
    if (stack.length > 0) {
        activity();
        back();
    } else {
        //if (oled.visible) oled.hide();
    }
}

exports.backButton = function() {
    if (stack.length > 0) {
        activity();
        if(exports.busy) return;
        beep.play(beepBack);
        back();
    } else {
        if (oled.visible) oled.hide();
    }
}

exports.set = function(object, key, value, callback) {
    return {
        type: "function",
        fn: function(arg, cb) {
            console.log("ui.set: " + key + " = " + value);
            object[key] = value;
            if(callback) {
                callback(cb);
            } else {
                cb && cb();
            }
        },
        selected: exports.select(object, key, value)
    }
}

exports.select = function(object, key, value) {
    return function() {
        return object && object[key] == value;
    }
}

exports.confirmationPrompt = function(promptText, optionText1, optionText2, helpText, callback1, callback2) {
    exports.load({
        name: promptText,
        origin: "prompt",
        type: "options",
        items: [{
            name: promptText,
            value: optionText1,
            help: helpText,
            action: {
                type: 'function',
                fn: function(arg, cb) {
                    if(callback1) {
                        callback1(cb);
                    } else {
                        back();
                        cb();
                    }
                }
            }
        }, {
            name: promptText,
            value: optionText2,
            help: helpText,
            action: {
                type: 'function',
                fn: function(arg, cb) {
                    if(callback2) {
                        callback2(cb);
                    } else {
                        back();
                        cb();
                    }
                }
            }
        }]
    }, null, null, true);
}

exports.defaultStatusString = "";
var statusTimeoutHandle = null;

exports.status = function(status) {
    if(statusTimeoutHandle) clearTimeout(statusTimeoutHandle);
    if(status) {
        oled.writeStatus(status);
        if(exports.busy) oled.showBusy();
        if(exports.defaultStatusString) {
            statusTimeoutHandle = setTimeout(function(){
                oled.writeStatus(exports.defaultStatusString);
                if(exports.busy) oled.showBusy();
            }, 6000);
        }
    } else {
        oled.writeStatus(exports.defaultStatusString);
        if(exports.busy) oled.showBusy();
    }
}

exports.defaultStatus = function(status) {
    exports.defaultStatusString = status;
}
<commit_msg>added beep<commit_after>var beep = require('interface/beep.js');
var _ = require('underscore');
var oled = null;
var currentProgram = null;
var backupProgram = null;
var currentName = "";
var stack = [];
var screenSaverHandle = null;

var beepEnter = beep.sine(2954, 0.04);
var beepBack = beep.sine(1447, 0.04);
var beepClick = beep.sine(738, 0.02);
var beepAlarm = beep.sine2(2954, 1540, 0.5);

exports.busy = false;

function activity() {
    oled.activity();
}

exports.init = function(menuController) {
    oled = menuController;
}

function load(program, selected) {
    if(!program) return;// exports.back();
    if (program.alternate && typeof program.alternate == "function") {
        var alternate = program.alternate();
        if (alternate !== false) {
            load(alternate, selected);
            return;
        }
    }

    currentName = program.name;
    currentProgram = _.clone(program);

    if (currentProgram.items && currentProgram.items.slice) {
        currentProgram.items = currentProgram.items.slice(0);
        currentProgram.items = currentProgram.items.filter(function(item) {
            if (typeof item.condition === "function") {
                return item.condition();
            } else {
                return true;
            }
        });
        for (var i = 0; i < currentProgram.items.length; i++) {
            var item = currentProgram.items[i];
            if (typeof item.name == 'function') {
                currentProgram.items[i] = _.clone(item);
                item = currentProgram.items[i];
                item.name = item.name();
            }
            if (selected == null) {
                if (item.selected && typeof item.selected === "function") {
                    if (item.selected()) {
                        selected = i;
                        break;
                    }
                } else if (item.action && item.action.selected && typeof item.action.selected === "function") {
                    if (item.action.selected()) {
                        selected = i;
                        break;
                    }
                }
            }
        }
        if(selected >= currentProgram.items.length) selected = currentProgram.items.length - 1;
    }
    if (currentProgram.type == "timelapse") {
        oled.setTimelapseMode(true);
    } else {
        oled.setTimelapseMode(false);
    }
    if (currentProgram.type == "menu" && currentProgram.hasImages == true) {
        oled.createMenuImage(currentProgram.items.map(function(item) {
            return {
                name: item.name,
                line2: item.line2,
                image: item.image
            };
        }), selected || 0);
        oled.update();
    } else if (currentProgram.type == "menu") {
        oled.create(currentProgram.items.map(function(item) {
            return item.name;
        }), selected || 0);
        oled.update();
    }
    if (currentProgram.type == "options") {
        oled.value(currentProgram.items, selected || 0);
        oled.update();
    }
    if (currentProgram.type == "textInput") {
        oled.text(currentProgram.name, currentProgram.value);
        oled.update();
    }
    if (currentProgram.type == "textDisplay") {
        oled.displayText(currentProgram.name, currentProgram.value);
        oled.update();
    }
    if (currentProgram.type == "png" && currentProgram.file) {
        oled.png(currentProgram.file);
    }
    if (currentProgram.type == "function" && currentProgram.fn) {
        currentProgram.fn(currentProgram.arg, function(err, program) {
            if(!err && program && (program.type)) {
                load(program);
            } else {
                console.log("function completed, going back");
                setTimeout(back);
            }
        });
    }
    if (currentProgram.type == "menuFunction" && currentProgram.fn) {
        currentProgram.fn(currentProgram.arg, function(err, p) {
            if (!err) exports.load(p, true);
        });
    }
}

exports.load = function(menuProgram, noPush, selected, forceStack) {
    exports.busy = false;
    if(currentProgram && currentProgram.intervalHandle) {
        clearInterval(currentProgram.intervalHandle)
        currentProgram.intervalHandle = null;
    }
    if ((forceStack && backupProgram != null) || (backupProgram != null && !noPush && backupProgram.type != "options" && backupProgram.type != "function")) {
        stack.push({
            program: backupProgram,
            selected: oled.selected,
            name: currentName
        });
    }
    backupProgram = menuProgram;

    if (typeof menuProgram == 'function') {
        exports.busy = true;
        oled.showBusy();
        menuProgram(function(arg, program) {
            exports.busy = false;
            load(program, selected);
        });
    } else {
        load(menuProgram, selected);
    }

}

exports.reload = function() {
    if (backupProgram.type == "menu" || backupProgram.type == "options") {
        exports.load(backupProgram, true, oled.selected);
    }
}

exports.up = function(alt) {
    activity();
    if(exports.busy) return;
    if (currentProgram.type == "menu" || currentProgram.type == "options") {
        oled.up();
    } else if(currentProgram.type == "textDisplay") {
        oled.up();
    } else if(currentProgram.type == "textInput") {
        if(alt) {
            oled.textMoveBackward();
        } else {
            oled.up();
        }
    }
}
exports.down = function(alt) {
    activity();
    if(exports.busy) return;
    if (currentProgram.type == "menu" || currentProgram.type == "options") {
        oled.down();
    } else if(currentProgram.type == "textDisplay") {
        oled.down();
    } else if(currentProgram.type == "textInput") {
        if(alt) {
            oled.textMoveForward();
        } else {
            oled.down();
        }
    }
}
exports.enter = function(alt) {
    activity();
    if(exports.busy) return;
    beep.play(beepEnter);
    if (currentProgram.type == "menu" || currentProgram.type == "options") {
        if(currentProgram.items[oled.selected]) exports.load(currentProgram.items[oled.selected].action);
    } else if (currentProgram.type == "textInput") {
        if(alt) {
            oled.textMoveForward();
        } else {
            //console.log("resulting string", oled.getTextValue());
            if(currentProgram.onSave) currentProgram.onSave(oled.getTextValue());
            back();
        }
    } else if (currentProgram.type == "png") {
        back();
    } else if (currentProgram.enter && typeof currentProgram.enter == "function") {
        currentProgram.enter();
    }
}
exports.help = function() {
    activity();
    if(exports.busy) return;
    beep.play(beepClick);
    if(currentProgram.type == "textDisplay" && currentProgram.origin == "help") {
        back();
    } else if (currentProgram.type == "menu" || currentProgram.type == "options") {
        if (currentProgram.items[oled.selected] && currentProgram.items[oled.selected].help) {
            exports.load({
                type: "textDisplay",
                origin: "help",
                name: "HELP - " + currentProgram.items[oled.selected].name || currentProgram.name,
                value: currentProgram.items[oled.selected].help
            });
        }
    } else if(currentProgram.help) {
        exports.load({
            type: "textDisplay",
            origin: "help",
            name: "HELP - " + currentProgram.name,
            value: currentProgram.help
        });
    }
}
exports.alert = function(title, text, updateInterval) {
    activity();
    var f, intervalHandle = null;
    if(typeof text === 'function') {
        f = text;
        text = f();
        intervalHandle = setInterval(function() {
            oled.updateDisplayText(f());
        }, updateInterval||1000);
    }
    exports.load({
        type: "textDisplay",
        origin: "alert",
        name: title,
        value: text,
        intervalHandle: intervalHandle
    });
}
exports.currentOrigin = function() {
    return currentProgram.origin;
}
exports.dismissAlert = function() {
    activity();
    if (currentProgram.type == "textDisplay" && currentProgram.origin == "alert" ) back();
}
exports.button3 = function() {
    activity();
    if(exports.busy) return;
    beep.play(beepClick);
    if (currentProgram.type == "menu" && currentProgram.items[oled.selected] && currentProgram.items[oled.selected].button3) {
        currentProgram.items[oled.selected].button3(currentProgram.items[oled.selected]);
    } else if (currentProgram.type == "textInput") {
        oled.textCycleMode();
    } else if (currentProgram.button3 && typeof currentProgram.button3 == "function") {
        currentProgram.button3();
    }
}
function back() {
    if(stack.length == 0) {
        console.log("already at menu top");
        return;
    }
    var b;
    do {
        b = stack.pop();
    } while((!b || !b.name) && stack.length > 0);
    if(currentProgram.origin == "prompt" && stack.length > 0) {
        do {
            b = stack.pop();
        } while((!b || !b.name) && stack.length > 0);
    }
    console.log("BACK to " + b.name);
    exports.load(b.program, true, b.selected);
}
exports.back = function() {
    exports.busy = false;
    if (stack.length > 0) {
        activity();
        back();
    } else {
        //if (oled.visible) oled.hide();
    }
}

exports.backButton = function() {
    if (stack.length > 0) {
        activity();
        if(exports.busy) return;
        beep.play(beepBack);
        back();
    } else {
        if (oled.visible) oled.hide();
    }
}

exports.set = function(object, key, value, callback) {
    return {
        type: "function",
        fn: function(arg, cb) {
            console.log("ui.set: " + key + " = " + value);
            object[key] = value;
            if(callback) {
                callback(cb);
            } else {
                cb && cb();
            }
        },
        selected: exports.select(object, key, value)
    }
}

exports.select = function(object, key, value) {
    return function() {
        return object && object[key] == value;
    }
}

exports.confirmationPrompt = function(promptText, optionText1, optionText2, helpText, callback1, callback2) {
    exports.load({
        name: promptText,
        origin: "prompt",
        type: "options",
        items: [{
            name: promptText,
            value: optionText1,
            help: helpText,
            action: {
                type: 'function',
                fn: function(arg, cb) {
                    if(callback1) {
                        callback1(cb);
                    } else {
                        back();
                        cb();
                    }
                }
            }
        }, {
            name: promptText,
            value: optionText2,
            help: helpText,
            action: {
                type: 'function',
                fn: function(arg, cb) {
                    if(callback2) {
                        callback2(cb);
                    } else {
                        back();
                        cb();
                    }
                }
            }
        }]
    }, null, null, true);
}

exports.defaultStatusString = "";
var statusTimeoutHandle = null;

exports.status = function(status) {
    if(statusTimeoutHandle) clearTimeout(statusTimeoutHandle);
    if(status) {
        oled.writeStatus(status);
        if(exports.busy) oled.showBusy();
        if(exports.defaultStatusString) {
            statusTimeoutHandle = setTimeout(function(){
                oled.writeStatus(exports.defaultStatusString);
                if(exports.busy) oled.showBusy();
            }, 6000);
        }
    } else {
        oled.writeStatus(exports.defaultStatusString);
        if(exports.busy) oled.showBusy();
    }
}

exports.defaultStatus = function(status) {
    exports.defaultStatusString = status;
}
<|endoftext|>


<commit_before>/*
Code for Life

Copyright (C) 2015, Ocado Limited

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

ADDITIONAL TERMS – Section 7 GNU General Public Licence

This licence does not grant any right, title or interest in any “Ocado” logos,
trade names or the trademark “Ocado” or any other trademarks or domain names
owned by Ocado Innovation Limited or the Ocado group of companies or any other
distinctive brand features of “Ocado” as may be secured from time to time. You
must not distribute any modification of this program using the trademark
“Ocado” or claim any affiliation or association with Ocado or its employees.

You are not authorised to use the name Ocado (or any of its trade names) or
the names of any author or contributor in advertising or for publicity purposes
pertaining to the distribution of this program, without the prior written
authorisation of Ocado.

Any propagation, distribution or conveyance of this program must include this
copyright notice and these terms. You must not misrepresent the origins of this
program; modified versions of the program must be marked as such and not
identified as the original program.
*/
var ocargo = ocargo || {};

ocargo.Saving = function() {
    this.getListOfWorkspacesFromLocalStorage = function() {
        var query = /blocklySavedWorkspaceXml-([0-9]+)$/;
        var i, results = [];
        for (i in localStorage) {
            if (localStorage.hasOwnProperty(i)) {
                var matches = query.exec(i);
                if (matches) {
                    var json = JSON.parse(localStorage.getItem(i));
                    results.push({
                        id: matches[1],
                        name: json.name,
                        workspace: json.workspace
                    });
                }
            }
        }
        return results;
    };
};

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

/************************/
/* Workspaces (game.js) */
/************************/

ocargo.Saving.prototype.retrieveListOfWorkspaces = function(callback) {
	if (USER_STATUS === 'TEACHER' ||  USER_STATUS === 'SCHOOL_STUDENT' || USER_STATUS === 'INDEPENDENT_STUDENT') {
		$.ajax({
	        url: '/rapidrouter/workspace/load_list/',
	        type: 'GET',
	        dataType: 'json',
	        success: function(json) {
	        	callback(null, json);
	        },
	        error: function(xhr,errmsg,err) {
	        	callback(xhr.status + ": " + errmsg + " " + err + " " + xhr.responseText);
	        }
	    });
	}
	else if (localStorage) {
		var results = this.getListOfWorkspacesFromLocalStorage();
	  	callback(null, results);
	}
    else {
		callback("Not logged in and no local storage available");
	}
};

ocargo.Saving.prototype.retrieveWorkspace = function(id, callback) {
	if (USER_STATUS === 'TEACHER' ||  USER_STATUS === 'SCHOOL_STUDENT' || USER_STATUS === 'INDEPENDENT_STUDENT') {
		$.ajax({
            url: '/rapidrouter/workspace/load/' + id + '/',
            type: 'GET',
            dataType: 'json',
            success: function(json) {
                callback(null, json);
            },
            error: function(xhr,errmsg,err) {
                callback(xhr.status + ": " + errmsg + " " + err + " " + xhr.responseText);
            }
        });
	}
    //setTimeout is used here in order to ensure that these lines of code are executed after the correct tab is selected
    else if (localStorage) {
        setTimeout(function(){
            var json = JSON.parse(localStorage.getItem('blocklySavedWorkspaceXml-' + id));
            callback(null, json);
        }, 0)
	}
    else {
		callback("Not logged in and no local storage available");
	}
};

ocargo.Saving.prototype.deleteWorkspace = function(id, callback) {
    csrftoken = $.cookie('csrftoken');
	if (USER_STATUS === 'TEACHER' ||  USER_STATUS === 'SCHOOL_STUDENT' || USER_STATUS === 'INDEPENDENT_STUDENT') {
		$.ajax({
            url: '/rapidrouter/workspace/delete/' + id + '/',
            type: 'POST',
            dataType: 'json',
            beforeSend: function(xhr, settings) {
                if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                    xhr.setRequestHeader("X-CSRFToken", csrftoken);
                }
            },
            success: function(json) {
                callback(null, json);
            },
            error: function(xhr,errmsg,err) {
                callback(xhr.status + ": " + errmsg + " " + err + " " + xhr.responseText);
            }
        });
	} else if (localStorage) {
		localStorage.removeItem('blocklySavedWorkspaceXml-' + id);
        var results = this.getListOfWorkspacesFromLocalStorage();
		callback(null, results);
	} else {
		callback("Not logged in and no local storage available");
	}
};

ocargo.Saving.prototype.saveWorkspace = function(workspace, id, callback) {
    csrftoken = $.cookie('csrftoken');
	if (USER_STATUS === 'TEACHER' ||  USER_STATUS === 'SCHOOL_STUDENT' || USER_STATUS === 'INDEPENDENT_STUDENT') {
		$.ajax({
            url: '/rapidrouter/workspace/save/' + (id ? (id + '/') : ''),
            type: 'POST',
            dataType: 'json',
            data: workspace,
            beforeSend: function(xhr, settings) {
                if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                    xhr.setRequestHeader("X-CSRFToken", csrftoken);
                }
            },
            success: function(json) {
                callback(null, json);
            },
            error: function(xhr,errmsg,err) {
                callback(xhr.status + ": " + errmsg + " " + err + " " + xhr.responseText);
            }
        });
	}
    else if (localStorage) {
		if(!id) {
            // Need to generate a unique integer, for our purposes this should do
            id = new Date().getTime();
        }

		localStorage.setItem('blocklySavedWorkspaceXml-' + id, JSON.stringify(workspace));

        var results = this.getListOfWorkspacesFromLocalStorage();
        callback(null, results);
	}
    else {
		callback("Not logged in and no local storage available");
	}
};

/****************************/
/* Levels (level_editor.js) */
/****************************/

ocargo.Saving.prototype._retrieveLevels = function(suffix, callback, errorCallback) {
    csrftoken = $.cookie('csrftoken');
    $.ajax({
        url: '/rapidrouter/level_editor/levels/' + suffix + '/',
        type: 'GET',
        dataType: 'json',
        beforeSend: function (xhr, settings) {
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
        },
        success: function (json) {
            callback(json);
        },
        error: function (xhr, errmsg, err) {
            errorCallback(xhr.status + ": " + errmsg + " " + err + " " + xhr.responseText);
        }
    });
};

ocargo.Saving.prototype.retrieveOwnedLevels = function(callback, errorCallback) {
    this._retrieveLevels('owned', callback, errorCallback);
};

ocargo.Saving.prototype.retrieveSharedLevels = function(callback, errorCallback) {
    this._retrieveLevels('shared', callback, errorCallback);
};

ocargo.Saving.prototype.retrieveLevel = function(id, callback) {
    csrftoken = $.cookie('csrftoken');
	$.ajax({
        url: '/rapidrouter/level_editor/level/get/' + id + '/',
        type: 'GET',
        dataType: 'json',
        beforeSend: function(xhr, settings) {
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
        },
        success: function(json) {
            callback(null, json.level, json.owned);
        },
        error: function(xhr,errmsg,err) {
            callback(xhr.status + ": " + errmsg + " " + err + " " + xhr.responseText);
        }
    });
};

ocargo.Saving.prototype.retrieveRandomLevel = function(data, callback) {
    csrftoken = $.cookie('csrftoken');
    $.ajax({
        url: "/rapidrouter/level_editor/level/random/",
        type: "GET",
        dataType: 'json',
        data: data,
        beforeSend: function(xhr, settings) {
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
        },
        success: function(json) {
            callback(null, json);
        },
        error: function(xhr, errmsg, err) {
           callback(xhr.status + ": " + errmsg + " " + err + " " + xhr.responseText);
        }
    });
};

 ocargo.Saving.prototype.deleteLevel = function(id, callback) {
    csrftoken = $.cookie('csrftoken');
	$.ajax({
        url: '/rapidrouter/level_editor/level/delete/' + id + '/',
        type: 'POST',
        dataType: 'json',
        data: {csrfmiddlewaretoken : $.cookie('csrftoken')},
        beforeSend: function(xhr, settings) {
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
        },
        success: function(json) {
            callback(null, json.ownedLevels, json.sharedLevels);
        },
        error: function(xhr,errmsg,err) {
            callback(xhr.status + ": " + errmsg + " " + err + " " + xhr.responseText);
        }
    });
};

ocargo.Saving.prototype.saveLevel = function(level, id, anonymous, callback) {
    csrftoken = $.cookie('csrftoken');
    level.anonymous = anonymous;
	$.ajax({
        url: '/rapidrouter/level_editor/level/save/' + (id ? (id + '/') : ''),
        type: 'POST',
        dataType: 'json',
        data: {data: JSON.stringify(level)},
        beforeSend: function(xhr, settings) {
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
        },
        success: function(json) {
            callback(null, json.levelID, json.ownedLevels, json.sharedLevels);
        },
        error: function(xhr,errmsg,err) {
            callback(xhr.status + ": " + errmsg + " " + err + " " + xhr.responseText);
        }
    });
    delete level.anonymous;
};

ocargo.Saving.prototype.getSharingInformation = function(levelID, callback) {
    csrftoken = $.cookie('csrftoken');
    $.ajax({
        url: '/rapidrouter/level_editor/level/get_sharing_information/' + levelID + '/',
        type: 'GET',
        dataType: 'json',
        beforeSend: function(xhr, settings) {
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
        },
        success: function(json) {
            callback(null, json);
        },
        error: function(xhr,errmsg,err) {
            callback(xhr.status + ": " + errmsg + " " + err + " " + xhr.responseText);
        }
    });
};

ocargo.Saving.prototype.shareLevel = function(levelID, recipientData, callback) {
    csrftoken = $.cookie('csrftoken');
    $.ajax({
        url: '/rapidrouter/level_editor/level/share/' + levelID + '/',
        type: 'POST',
        dataType: 'json',
        data: recipientData,
        beforeSend: function(xhr, settings) {
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
        },
        success: function(json) {
            callback(null, json);
        },
        error: function(xhr,errmsg,err) {
            callback(xhr.status + ": " + errmsg + " " + err + " " + xhr.responseText);
        }
    });
    delete recipientData.csrfmiddlewaretoken;
};
<commit_msg>Code formatting.<commit_after>/*
 Code for Life

 Copyright (C) 2015, Ocado Limited

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as
 published by the Free Software Foundation, either version 3 of the
 License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU Affero General Public License for more details.

 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.

 ADDITIONAL TERMS – Section 7 GNU General Public Licence

 This licence does not grant any right, title or interest in any “Ocado” logos,
 trade names or the trademark “Ocado” or any other trademarks or domain names
 owned by Ocado Innovation Limited or the Ocado group of companies or any other
 distinctive brand features of “Ocado” as may be secured from time to time. You
 must not distribute any modification of this program using the trademark
 “Ocado” or claim any affiliation or association with Ocado or its employees.

 You are not authorised to use the name Ocado (or any of its trade names) or
 the names of any author or contributor in advertising or for publicity purposes
 pertaining to the distribution of this program, without the prior written
 authorisation of Ocado.

 Any propagation, distribution or conveyance of this program must include this
 copyright notice and these terms. You must not misrepresent the origins of this
 program; modified versions of the program must be marked as such and not
 identified as the original program.
 */
var ocargo = ocargo || {};

ocargo.Saving = function () {
    this.getListOfWorkspacesFromLocalStorage = function () {
        var query = /blocklySavedWorkspaceXml-([0-9]+)$/;
        var i, results = [];
        for (i in localStorage) {
            if (localStorage.hasOwnProperty(i)) {
                var matches = query.exec(i);
                if (matches) {
                    var json = JSON.parse(localStorage.getItem(i));
                    results.push({
                        id: matches[1],
                        name: json.name,
                        workspace: json.workspace
                    });
                }
            }
        }
        return results;
    };
};

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

/************************/
/* Workspaces (game.js) */
/************************/

ocargo.Saving.prototype.retrieveListOfWorkspaces = function (callback) {
    if (USER_STATUS === 'TEACHER' || USER_STATUS === 'SCHOOL_STUDENT' || USER_STATUS === 'INDEPENDENT_STUDENT') {
        $.ajax({
            url: '/rapidrouter/workspace/load_list/',
            type: 'GET',
            dataType: 'json',
            success: function (json) {
                callback(null, json);
            },
                  error: function (xhr, errmsg, err) {
          callback(xhr.status + ": " + errmsg + " " + err + " " + xhr.responseText);
            }
        });
    }
    else if (localStorage) {
        var results = this.getListOfWorkspacesFromLocalStorage();
        callback(null, results);
    }
    else {
        callback("Not logged in and no local storage available");
    }
};

ocargo.Saving.prototype.retrieveWorkspace = function (id, callback) {
    if (USER_STATUS === 'TEACHER' || USER_STATUS === 'SCHOOL_STUDENT' || USER_STATUS === 'INDEPENDENT_STUDENT') {
        $.ajax({
            url: '/rapidrouter/workspace/load/' + id + '/',
            type: 'GET',
            dataType: 'json',
            success: function (json) {
                callback(null, json);
            },
            error: function (xhr, errmsg, err) {
                callback(xhr.status + ": " + errmsg + " " + err + " " + xhr.responseText);
            }
        });
    }
    //setTimeout is used here in order to ensure that these lines of code are executed after the correct tab is selected
    else if (localStorage) {
        setTimeout(function () {
            var json = JSON.parse(localStorage.getItem('blocklySavedWorkspaceXml-' + id));
            callback(null, json);
        }, 0)
    }
    else {
        callback("Not logged in and no local storage available");
    }
};

ocargo.Saving.prototype.deleteWorkspace = function (id, callback) {
    csrftoken = $.cookie('csrftoken');
    if (USER_STATUS === 'TEACHER' || USER_STATUS === 'SCHOOL_STUDENT' || USER_STATUS === 'INDEPENDENT_STUDENT') {
        $.ajax({
            url: '/rapidrouter/workspace/delete/' + id + '/',
            type: 'POST',
            dataType: 'json',
            beforeSend: function (xhr, settings) {
                if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                    xhr.setRequestHeader("X-CSRFToken", csrftoken);
                }
            },
            success: function (json) {
                callback(null, json);
            },
            error: function (xhr, errmsg, err) {
                callback(xhr.status + ": " + errmsg + " " + err + " " + xhr.responseText);
            }
        });
    } else if (localStorage) {
        localStorage.removeItem('blocklySavedWorkspaceXml-' + id);
        var results = this.getListOfWorkspacesFromLocalStorage();
        callback(null, results);
    } else {
        callback("Not logged in and no local storage available");
    }
};

ocargo.Saving.prototype.saveWorkspace = function (workspace, id, callback) {
    csrftoken = $.cookie('csrftoken');
    if (USER_STATUS === 'TEACHER' || USER_STATUS === 'SCHOOL_STUDENT' || USER_STATUS === 'INDEPENDENT_STUDENT') {
        $.ajax({
            url: '/rapidrouter/workspace/save/' + (id ? (id + '/') : ''),
            type: 'POST',
            dataType: 'json',
            data: workspace,
            beforeSend: function (xhr, settings) {
                if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                    xhr.setRequestHeader("X-CSRFToken", csrftoken);
                }
            },
            success: function (json) {
                callback(null, json);
            },
            error: function (xhr, errmsg, err) {
                callback(xhr.status + ": " + errmsg + " " + err + " " + xhr.responseText);
            }
        });
    }
    else if (localStorage) {
        if (!id) {
            // Need to generate a unique integer, for our purposes this should do
            id = new Date().getTime();
        }

        localStorage.setItem('blocklySavedWorkspaceXml-' + id, JSON.stringify(workspace));

        var results = this.getListOfWorkspacesFromLocalStorage();
        callback(null, results);
    }
    else {
        callback("Not logged in and no local storage available");
    }
};

/****************************/
/* Levels (level_editor.js) */
/****************************/

ocargo.Saving.prototype._retrieveLevels = function (suffix, callback, errorCallback) {
    csrftoken = $.cookie('csrftoken');
    $.ajax({
        url: '/rapidrouter/level_editor/levels/' + suffix + '/',
        type: 'GET',
        dataType: 'json',
        beforeSend: function (xhr, settings) {
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
        },
        success: function (json) {
            callback(json);
        },
        error: function (xhr, errmsg, err) {
            errorCallback(xhr.status + ": " + errmsg + " " + err + " " + xhr.responseText);
        }
    });
};

ocargo.Saving.prototype.retrieveOwnedLevels = function (callback, errorCallback) {
    this._retrieveLevels('owned', callback, errorCallback);
};

ocargo.Saving.prototype.retrieveSharedLevels = function (callback, errorCallback) {
    this._retrieveLevels('shared', callback, errorCallback);
};

ocargo.Saving.prototype.retrieveLevel = function (id, callback) {
    csrftoken = $.cookie('csrftoken');
    $.ajax({
        url: '/rapidrouter/level_editor/level/get/' + id + '/',
        type: 'GET',
        dataType: 'json',
        beforeSend: function (xhr, settings) {
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
        },
        success: function (json) {
            callback(null, json.level, json.owned);
        },
        error: function (xhr, errmsg, err) {
            callback(xhr.status + ": " + errmsg + " " + err + " " + xhr.responseText);
        }
    });
};

ocargo.Saving.prototype.retrieveRandomLevel = function (data, callback) {
    csrftoken = $.cookie('csrftoken');
    $.ajax({
        url: "/rapidrouter/level_editor/level/random/",
        type: "GET",
        dataType: 'json',
        data: data,
        beforeSend: function (xhr, settings) {
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
        },
        success: function (json) {
            callback(null, json);
        },
        error: function (xhr, errmsg, err) {
            callback(xhr.status + ": " + errmsg + " " + err + " " + xhr.responseText);
        }
    });
};

ocargo.Saving.prototype.deleteLevel = function (id, callback) {
    csrftoken = $.cookie('csrftoken');
    $.ajax({
        url: '/rapidrouter/level_editor/level/delete/' + id + '/',
        type: 'POST',
        dataType: 'json',
        data: {csrfmiddlewaretoken: $.cookie('csrftoken')},
        beforeSend: function (xhr, settings) {
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
        },
        success: function (json) {
            callback(null, json.ownedLevels, json.sharedLevels);
        },
        error: function (xhr, errmsg, err) {
            callback(xhr.status + ": " + errmsg + " " + err + " " + xhr.responseText);
        }
    });
};

ocargo.Saving.prototype.saveLevel = function (level, id, anonymous, callback) {
    csrftoken = $.cookie('csrftoken');
    level.anonymous = anonymous;
    $.ajax({
        url: '/rapidrouter/level_editor/level/save/' + (id ? (id + '/') : ''),
        type: 'POST',
        dataType: 'json',
        data: {data: JSON.stringify(level)},
        beforeSend: function (xhr, settings) {
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
        },
        success: function (json) {
            callback(null, json.levelID, json.ownedLevels, json.sharedLevels);
        },
        error: function (xhr, errmsg, err) {
            callback(xhr.status + ": " + errmsg + " " + err + " " + xhr.responseText);
        }
    });
    delete level.anonymous;
};

ocargo.Saving.prototype.getSharingInformation = function (levelID, callback) {
    csrftoken = $.cookie('csrftoken');
    $.ajax({
        url: '/rapidrouter/level_editor/level/get_sharing_information/' + levelID + '/',
        type: 'GET',
        dataType: 'json',
        beforeSend: function (xhr, settings) {
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
        },
        success: function (json) {
            callback(null, json);
        },
        error: function (xhr, errmsg, err) {
            callback(xhr.status + ": " + errmsg + " " + err + " " + xhr.responseText);
        }
    });
};

ocargo.Saving.prototype.shareLevel = function (levelID, recipientData, callback) {
    csrftoken = $.cookie('csrftoken');
    $.ajax({
        url: '/rapidrouter/level_editor/level/share/' + levelID + '/',
        type: 'POST',
        dataType: 'json',
        data: recipientData,
        beforeSend: function (xhr, settings) {
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
        },
        success: function (json) {
            callback(null, json);
        },
        error: function (xhr, errmsg, err) {
            callback(xhr.status + ": " + errmsg + " " + err + " " + xhr.responseText);
        }
    });
    delete recipientData.csrfmiddlewaretoken;
};
<|endoftext|>


<commit_before>var http = require('http');
var utils = require('util');
var path = require('path');
var fs = require('fs');
var sqlite3 = require('sqlite3').verbose();
var db = new sqlite3.Database(':memory:');

var data;
fs.readFile('./MockData.json', 'utf8', function (err, data) {
    if (err) {
        console.log("ERROR: JSON load - " + err);
        throw err;
    } else {
        try {
            data = JSON.parse(data);
            console.log("JSON loaded successfully");
            var parsed = JSON.parse(data);
            console.log(parsed);
            var pet = data["mockdata"]["pets"][0];
            console.log(pet);

	    console.log(data);
        }
        catch (ex) {
            console.log("ERROR: JSON parse - " + err);
        }

    }
});
<commit_msg>--4am parsing shenanigans<commit_after>var http = require('http');
var utils = require('util');
var path = require('path');
var fs = require('fs');
var sqlite3 = require('sqlite3').verbose();
var db = new sqlite3.Database(':memory:');

var data;
fs.readFile('./MockData.json', 'utf8', function (err, data) {
    if (err) {
        console.log("ERROR: JSON load - " + err);
        throw err;
    } else {
        try {
            data = JSON.parse(data);
            console.log("JSON loaded successfully");
            var obj = JSON.parse(data);
            console.log(obj.data);
            //var pet = data["mockdata"]["pets"][0];
            //console.log(pet);

	    console.log(data);
        }
        catch (ex) {
            console.log("ERROR: JSON parse - " + err);
        }

    }
});
<|endoftext|>


<commit_before>/* eslint-disable no-var, camelcase, import/newline-after-import */

const child_process = require("child_process");

const packages = [
  "babel-core",
  "babel-eslint",
  "babel-loader",
  "babel-plugin-transform-class-properties",
  "babel-plugin-transform-object-rest-spread",
  "babel-preset-es2015",
  "babel-preset-react",
  "css-loader",
  "eslint",
  "eslint-config-prettier",
  "eslint-config-react-app",
  "eslint-plugin-flowtype",
  "eslint-plugin-import",
  "eslint-plugin-jsx-a11y",
  "eslint-plugin-react",
  "file-loader",
  "flow-bin",
  "less",
  "less-loader",
  "react-hot-loader@next", // RHL-3 is currently in beta
  "style-loader",
  "uglifyjs-webpack-plugin",
  "url-loader",
  "webpack",
  "webpack-dashboard",
  "webpack-dev-server"
];

const argv = process.argv.slice(2);
const isYarn = argv.indexOf("--yarn") !== -1;
const isLatest = argv.indexOf("--latest") !== -1;

function getPackages() {
  return packages.map(_p => {
    if (_p === "react-hot-loader@next") return _p;
    return isLatest ? _p + "@latest" : _p;
  });
}

if (isYarn) {
  child_process.spawn("yarn", ["add", "-D"].concat(getPackages()), {
    stdio: "inherit"
  });
} else {
  child_process.spawn("npm", ["i", "-D"].concat(getPackages()), {
    stdio: "inherit"
  });
}
<commit_msg>chore(install): Added missing `uglify-js` dependency<commit_after>/* eslint-disable no-var, camelcase, import/newline-after-import */

const child_process = require("child_process");

const packages = [
  "babel-core",
  "babel-eslint",
  "babel-loader",
  "babel-plugin-transform-class-properties",
  "babel-plugin-transform-object-rest-spread",
  "babel-preset-es2015",
  "babel-preset-react",
  "css-loader",
  "eslint",
  "eslint-config-prettier",
  "eslint-config-react-app",
  "eslint-plugin-flowtype",
  "eslint-plugin-import",
  "eslint-plugin-jsx-a11y",
  "eslint-plugin-react",
  "file-loader",
  "flow-bin",
  "less",
  "less-loader",
  "react-hot-loader@next", // RHL-3 is currently in beta
  "style-loader",
  "uglify-js@2.8.23", // UglifyJs 3 is not compatible with uglifyjs-webpack-plugin
  "uglifyjs-webpack-plugin",
  "url-loader",
  "webpack",
  "webpack-dashboard",
  "webpack-dev-server"
];

const argv = process.argv.slice(2);
const isYarn = argv.indexOf("--yarn") !== -1;
const isLatest = argv.indexOf("--latest") !== -1;

function getPackages() {
  return packages.map(_p => {
    if (_p === "react-hot-loader@next") return _p;
    return isLatest ? _p + "@latest" : _p;
  });
}

if (isYarn) {
  child_process.spawn("yarn", ["add", "-D"].concat(getPackages()), {
    stdio: "inherit"
  });
} else {
  child_process.spawn("npm", ["i", "-D"].concat(getPackages()), {
    stdio: "inherit"
  });
}
<|endoftext|>


<commit_before>function hereDoc(f) {
    return f.toString().
        replace(/^[^\/]+\/\*!?/, '').
        replace(/\*\/[^\/]+$/, '');
}

module.exports.deb = hereDoc(function()
{/*!
Run these commands on your Linux Ubuntu/Debian-based hosts, it'll install our self-updating collector agent:

echo "deb http://repo.logdna.com stable main" | sudo tee /etc/apt/sources.list.d/logdna.list
wget -O- https://s3.amazonaws.com/repo.logdna.com/logdna.gpg | sudo apt-key add -
sudo apt-get update
sudo apt-get install logdna-agent < "/dev/null" # this line needed for copy/paste
sudo logdna-agent -k ZZZZZZZZ # this is your unique API Key
# /var/log is monitored/added by default (recursively), optionally add more dirs here
sudo logdna-agent -d /path/to/log/folders
sudo update-rc.d logdna-agent defaults
sudo /etc/init.d/logdna-agent start

Once shipping begins, you can tail using 'logdna tail' or 'logdna tail --help' for more info
*/});

module.exports.rpm = hereDoc(function()
{/*!
Run these commands on your Linux RPM-based hosts, it'll install our self-updating collector agent:

echo "[logdna]
name=LogDNA packages
baseurl=http://repo.logdna.com/el6/
enabled=1
gpgcheck=0" | sudo tee /etc/yum.repos.d/logdna.repo
sudo yum -y install logdna-agent
sudo logdna-agent -k ZZZZZZZZ # this is your unique API Key
# /var/log is monitored/added by default (recursively), optionally add more dirs here
sudo logdna-agent -d /path/to/log/folders
sudo chkconfig logdna-agent on
sudo service logdna-agent start

Once shipping begins, you can tail using 'logdna tail' or 'logdna tail --help' for more info
*/});

module.exports.windows = hereDoc(function()
{/*!
Run these commands on your Windows hosts using Command Prompt (cmd.exe), it'll install our self-updating collector agent:

@powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))" && SET PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin
choco install logdna-agent -y
logdna-agent -k ZZZZZZZZ # this is your unique API Key
:: %ALLUSERSPROFILE%\logs is monitored/added by default (recursively), optionally add more dirs here
logdna-agent -d C:\path\to\log\folders
nssm start logdna-agent

Once shipping begins, you can tail using 'logdna tail' or 'logdna tail --help' for more info
*/});

module.exports.mac = hereDoc(function()
{/*!
These commands require Homebrew package manager (http://brew.sh/).
Run these commands on your macOS Server hosts, it'll install our self-updating collector agent:

brew update
brew cask install logdna-agent
sudo logdna-agent -k ZZZZZZZZ # this is your unique API Key
# /var/log is monitored/added by default (recursively), optionally add more dirs here
sudo logdna-agent -d /path/to/log/folders
# Optional: Have logdna-agent always run in the background
sudo launchctl load -w /Library/LaunchDaemons/com.logdna.logdna-agent.plist

Once shipping begins, you can tail using 'logdna tail' or 'logdna tail --help' for more info
*/});

module.exports.heroku = hereDoc(function()
{/*!
The LogDNA add-on is available through the Heroku Elements Marketplace.

1) Sign up for an account first using 'logdna register'
2) Install the add-on here: https://elements.heroku.com/addons/logdna

Once shipping begins, you can tail using 'logdna tail' or 'logdna tail --help' for more info
*/});

module.exports['heroku-drains'] = hereDoc(function()
{/*!
Use 'logdna heroku <heroku-app-name>' to generate a Heroku Drain URL.
*/});

module.exports.syslog = hereDoc(function()
{/*!
We support ingesting logs from rsyslog, syslog-ng, and plain 'ol syslog. Secure TLS, custom ports, etc.

1) Sign up for an account first using 'logdna register'
2) Follow syslog instructions here: https://app.logdna.com/pages/add-host

Once shipping begins, you can tail using 'logdna tail' or 'logdna tail --help' for more info
*/});

module.exports.api = hereDoc(function()
{/*!
Send us your logs via our REST-based ingestion API.

1) Sign up for an account first using 'logdna register'
2) See our docs here: https://docs.logdna.com/docs/api

Once shipping begins, you can tail using 'logdna tail' or 'logdna tail --help' for more info
*/});

module.exports.nodejs = hereDoc(function()
{/*!
Check out our official Node.js library. Supports both direct logging and integration via Winston.

1) Sign up for an account first using 'logdna register'
2) See our GitHub repo here: https://github.com/logdna/nodejs

Once shipping begins, you can tail using 'logdna tail' or 'logdna tail --help' for more info
*/});

module.exports.k8s = hereDoc(function()
{/*!
Ship logs from your Kubernetes v1.2+ cluster:

1) Sign up for an account first using 'logdna register'
2) Run: kubectl create secret generic logdna-agent-key --from-literal=logdna-agent-key=ZZZZZZZZ
3) Run: kubectl create -f https://raw.githubusercontent.com/logdna/logdna-agent/master/logdna-agent-ds.yaml

Once shipping begins, you can tail using 'logdna tail' or 'logdna tail --help' for more info
*/});

module.exports.docker = hereDoc(function()
{/*!
Ship logs from Docker containers:

1) Sign up for an account first using 'logdna register'
2) Follow Docker instructions here: https://app.logdna.com/pages/add-host

Once shipping begins, you can tail using 'logdna tail' or 'logdna tail --help' for more info
*/});
<commit_msg>Change API Key to Ingestion Key in install.js<commit_after>function hereDoc(f) {
    return f.toString().
        replace(/^[^\/]+\/\*!?/, '').
        replace(/\*\/[^\/]+$/, '');
}

module.exports.deb = hereDoc(function()
{/*!
Run these commands on your Linux Ubuntu/Debian-based hosts, it'll install our self-updating collector agent:

echo "deb http://repo.logdna.com stable main" | sudo tee /etc/apt/sources.list.d/logdna.list
wget -O- https://s3.amazonaws.com/repo.logdna.com/logdna.gpg | sudo apt-key add -
sudo apt-get update
sudo apt-get install logdna-agent < "/dev/null" # this line needed for copy/paste
sudo logdna-agent -k ZZZZZZZZ # this is your unique Ingestion Key
# /var/log is monitored/added by default (recursively), optionally add more dirs here
sudo logdna-agent -d /path/to/log/folders
sudo update-rc.d logdna-agent defaults
sudo /etc/init.d/logdna-agent start

Once shipping begins, you can tail using 'logdna tail' or 'logdna tail --help' for more info
*/});

module.exports.rpm = hereDoc(function()
{/*!
Run these commands on your Linux RPM-based hosts, it'll install our self-updating collector agent:

echo "[logdna]
name=LogDNA packages
baseurl=http://repo.logdna.com/el6/
enabled=1
gpgcheck=0" | sudo tee /etc/yum.repos.d/logdna.repo
sudo yum -y install logdna-agent
sudo logdna-agent -k ZZZZZZZZ # this is your unique Ingestion Key
# /var/log is monitored/added by default (recursively), optionally add more dirs here
sudo logdna-agent -d /path/to/log/folders
sudo chkconfig logdna-agent on
sudo service logdna-agent start

Once shipping begins, you can tail using 'logdna tail' or 'logdna tail --help' for more info
*/});

module.exports.windows = hereDoc(function()
{/*!
Run these commands on your Windows hosts using Command Prompt (cmd.exe), it'll install our self-updating collector agent:

@powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))" && SET PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin
choco install logdna-agent -y
logdna-agent -k ZZZZZZZZ # this is your unique Ingestion Key
:: %ALLUSERSPROFILE%\logs is monitored/added by default (recursively), optionally add more dirs here
logdna-agent -d C:\path\to\log\folders
nssm start logdna-agent

Once shipping begins, you can tail using 'logdna tail' or 'logdna tail --help' for more info
*/});

module.exports.mac = hereDoc(function()
{/*!
These commands require Homebrew package manager (http://brew.sh/).
Run these commands on your macOS Server hosts, it'll install our self-updating collector agent:

brew update
brew cask install logdna-agent
sudo logdna-agent -k ZZZZZZZZ # this is your unique Ingestion Key
# /var/log is monitored/added by default (recursively), optionally add more dirs here
sudo logdna-agent -d /path/to/log/folders
# Optional: Have logdna-agent always run in the background
sudo launchctl load -w /Library/LaunchDaemons/com.logdna.logdna-agent.plist

Once shipping begins, you can tail using 'logdna tail' or 'logdna tail --help' for more info
*/});

module.exports.heroku = hereDoc(function()
{/*!
The LogDNA add-on is available through the Heroku Elements Marketplace.

1) Sign up for an account first using 'logdna register'
2) Install the add-on here: https://elements.heroku.com/addons/logdna

Once shipping begins, you can tail using 'logdna tail' or 'logdna tail --help' for more info
*/});

module.exports['heroku-drains'] = hereDoc(function()
{/*!
Use 'logdna heroku <heroku-app-name>' to generate a Heroku Drain URL.
*/});

module.exports.syslog = hereDoc(function()
{/*!
We support ingesting logs from rsyslog, syslog-ng, and plain 'ol syslog. Secure TLS, custom ports, etc.

1) Sign up for an account first using 'logdna register'
2) Follow syslog instructions here: https://app.logdna.com/pages/add-host

Once shipping begins, you can tail using 'logdna tail' or 'logdna tail --help' for more info
*/});

module.exports.api = hereDoc(function()
{/*!
Send us your logs via our REST-based ingestion API.

1) Sign up for an account first using 'logdna register'
2) See our docs here: https://docs.logdna.com/docs/api

Once shipping begins, you can tail using 'logdna tail' or 'logdna tail --help' for more info
*/});

module.exports.nodejs = hereDoc(function()
{/*!
Check out our official Node.js library. Supports both direct logging and integration via Winston.

1) Sign up for an account first using 'logdna register'
2) See our GitHub repo here: https://github.com/logdna/nodejs

Once shipping begins, you can tail using 'logdna tail' or 'logdna tail --help' for more info
*/});

module.exports.k8s = hereDoc(function()
{/*!
Ship logs from your Kubernetes v1.2+ cluster:

1) Sign up for an account first using 'logdna register'
2) Run: kubectl create secret generic logdna-agent-key --from-literal=logdna-agent-key=ZZZZZZZZ
3) Run: kubectl create -f https://raw.githubusercontent.com/logdna/logdna-agent/master/logdna-agent-ds.yaml

Once shipping begins, you can tail using 'logdna tail' or 'logdna tail --help' for more info
*/});

module.exports.docker = hereDoc(function()
{/*!
Ship logs from Docker containers:

1) Sign up for an account first using 'logdna register'
2) Follow Docker instructions here: https://app.logdna.com/pages/add-host

Once shipping begins, you can tail using 'logdna tail' or 'logdna tail --help' for more info
*/});
<|endoftext|>