<commit_before>/* FileSaver.js
 * A saveAs() FileSaver implementation.
 * 1.1.20150716
 *
 * By Eli Grey, http://eligrey.com
 * License: X11/MIT
 *   See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
 */

/*global self */
/*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */

/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */

var saveAs = saveAs || (function(view) {
	"use strict";
	// IE <10 is explicitly unsupported
	if (typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
		return;
	}
	var
		  doc = view.document
		  // only get URL when necessary in case Blob.js hasn't overridden it yet
		, get_URL = function() {
			return view.URL || view.webkitURL || view;
		}
		, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
		, can_use_save_link = "download" in save_link
		, click = function(node) {
			var event = new MouseEvent("click");
			node.dispatchEvent(event);
		}
		, webkit_req_fs = view.webkitRequestFileSystem
		, req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem
		, throw_outside = function(ex) {
			(view.setImmediate || view.setTimeout)(function() {
				throw ex;
			}, 0);
		}
		, force_saveable_type = "application/octet-stream"
		, fs_min_size = 0
		// See https://code.google.com/p/chromium/issues/detail?id=375297#c7 and
		// https://github.com/eligrey/FileSaver.js/commit/485930a#commitcomment-8768047
		// for the reasoning behind the timeout and revocation flow
		, arbitrary_revoke_timeout = 500 // in ms
		, revoke = function(file) {
			var revoker = function() {
				if (typeof file === "string") { // file is an object URL
					get_URL().revokeObjectURL(file);
				} else { // file is a File
					file.remove();
				}
			};
			if (view.chrome) {
				revoker();
			} else {
				setTimeout(revoker, arbitrary_revoke_timeout);
			}
		}
		, dispatch = function(filesaver, event_types, event) {
			event_types = [].concat(event_types);
			var i = event_types.length;
			while (i--) {
				var listener = filesaver["on" + event_types[i]];
				if (typeof listener === "function") {
					try {
						listener.call(filesaver, event || filesaver);
					} catch (ex) {
						throw_outside(ex);
					}
				}
			}
		}
		, auto_bom = function(blob) {
			// prepend BOM for UTF-8 XML and text/* types (including HTML)
			if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
				return new Blob(["\ufeff", blob], {type: blob.type});
			}
			return blob;
		}
		, FileSaver = function(blob, name, no_auto_bom) {
			if (!no_auto_bom) {
				blob = auto_bom(blob);
			}
			// First try a.download, then web filesystem, then object URLs
			var
				  filesaver = this
				, type = blob.type
				, blob_changed = false
				, object_url
				, target_view
				, dispatch_all = function() {
					dispatch(filesaver, "writestart progress write writeend".split(" "));
				}
				// on any filesys errors revert to saving with object URLs
				, fs_error = function() {
					if (target_view && typeof safari !== "undefined" && typeof FileReader !== "undefined") {
						// Safari doesn't allow downloading of blob urls
						var reader = new FileReader();
						reader.onloadend = function() {
							var base64Data = reader.result;
							base64Data = base64Data.replace(/^data:.*;/, 'data:attachment/file;');
							target_view.location.href = encodeURI(base64Data);
							filesaver.readyState = filesaver.DONE;
							dispatch_all();
						};
						reader.readAsDataURL(blob);
						filesaver.readyState = filesaver.INIT;
						return;
					}
					// don't create more object URLs than needed
					if (blob_changed || !object_url) {
						object_url = get_URL().createObjectURL(blob);
					}
					if (target_view) {
						target_view.location.href = object_url;
					} else {
						var new_tab = view.open(object_url, "_blank");
						if (new_tab == undefined && typeof safari !== "undefined") {
							//Apple do not allow window.open, see http://bit.ly/1kZffRI
							view.location.href = object_url
						}
					}
					filesaver.readyState = filesaver.DONE;
					dispatch_all();
					revoke(object_url);
				}
				, abortable = function(func) {
					return function() {
						if (filesaver.readyState !== filesaver.DONE) {
							return func.apply(this, arguments);
						}
					};
				}
				, create_if_not_found = {create: true, exclusive: false}
				, slice
			;
			filesaver.readyState = filesaver.INIT;
			if (!name) {
				name = "download";
			}
			if (can_use_save_link) {
				object_url = get_URL().createObjectURL(blob);
				save_link.href = object_url;
				save_link.download = name;
				setTimeout(function() {
					click(save_link);
					dispatch_all();
					revoke(object_url);
					filesaver.readyState = filesaver.DONE;
				});
				return;
			}
			// Object and web filesystem URLs have a problem saving in Google Chrome when
			// viewed in a tab, so I force save with application/octet-stream
			// http://code.google.com/p/chromium/issues/detail?id=91158
			// Update: Google errantly closed 91158, I submitted it again:
			// https://code.google.com/p/chromium/issues/detail?id=389642
			if (view.chrome && type && type !== force_saveable_type) {
				slice = blob.slice || blob.webkitSlice;
				blob = slice.call(blob, 0, blob.size, force_saveable_type);
				blob_changed = true;
			}
			// Since I can't be sure that the guessed media type will trigger a download
			// in WebKit, I append .download to the filename.
			// https://bugs.webkit.org/show_bug.cgi?id=65440
			if (webkit_req_fs && name !== "download") {
				name += ".download";
			}
			if (type === force_saveable_type || webkit_req_fs) {
				target_view = view;
			}
			if (!req_fs) {
				fs_error();
				return;
			}
			fs_min_size += blob.size;
			req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) {
				fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) {
					var save = function() {
						dir.getFile(name, create_if_not_found, abortable(function(file) {
							file.createWriter(abortable(function(writer) {
								writer.onwriteend = function(event) {
									target_view.location.href = file.toURL();
									filesaver.readyState = filesaver.DONE;
									dispatch(filesaver, "writeend", event);
									revoke(file);
								};
								writer.onerror = function() {
									var error = writer.error;
									if (error.code !== error.ABORT_ERR) {
										fs_error();
									}
								};
								"writestart progress write abort".split(" ").forEach(function(event) {
									writer["on" + event] = filesaver["on" + event];
								});
								writer.write(blob);
								filesaver.abort = function() {
									writer.abort();
									filesaver.readyState = filesaver.DONE;
								};
								filesaver.readyState = filesaver.WRITING;
							}), fs_error);
						}), fs_error);
					};
					dir.getFile(name, {create: false}, abortable(function(file) {
						// delete file if it already exists
						file.remove();
						save();
					}), abortable(function(ex) {
						if (ex.code === ex.NOT_FOUND_ERR) {
							save();
						} else {
							fs_error();
						}
					}));
				}), fs_error);
			}), fs_error);
		}
		, FS_proto = FileSaver.prototype
		, saveAs = function(blob, name, no_auto_bom) {
			return new FileSaver(blob, name, no_auto_bom);
		}
	;
	// IE 10+ (native saveAs)
	if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
		return function(blob, name, no_auto_bom) {
			if (!no_auto_bom) {
				blob = auto_bom(blob);
			}
			return navigator.msSaveOrOpenBlob(blob, name || "download");
		};
	}

	FS_proto.abort = function() {
		var filesaver = this;
		filesaver.readyState = filesaver.DONE;
		dispatch(filesaver, "abort");
	};
	FS_proto.readyState = FS_proto.INIT = 0;
	FS_proto.WRITING = 1;
	FS_proto.DONE = 2;

	FS_proto.error =
	FS_proto.onwritestart =
	FS_proto.onprogress =
	FS_proto.onwrite =
	FS_proto.onabort =
	FS_proto.onerror =
	FS_proto.onwriteend =
		null;

	return saveAs;
}(
	   typeof self !== "undefined" && self
	|| typeof window !== "undefined" && window
	|| this.content
));
// `self` is undefined in Firefox for Android content script context
// while `this` is nsIContentFrameMessageManager
// with an attribute `content` that corresponds to the window

if (typeof module !== "undefined" && module.exports) {
  module.exports.saveAs = saveAs;
} else if ((typeof define !== "undefined" && define !== null) && (define.amd != null)) {
  define([], function() {
    return saveAs;
  });
}
<commit_msg>better regex check<commit_after>/* FileSaver.js
 * A saveAs() FileSaver implementation.
 * 1.1.20150716
 *
 * By Eli Grey, http://eligrey.com
 * License: X11/MIT
 *   See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
 */

/*global self */
/*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */

/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */

var saveAs = saveAs || (function(view) {
	"use strict";
	// IE <10 is explicitly unsupported
	if (typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
		return;
	}
	var
		  doc = view.document
		  // only get URL when necessary in case Blob.js hasn't overridden it yet
		, get_URL = function() {
			return view.URL || view.webkitURL || view;
		}
		, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
		, can_use_save_link = "download" in save_link
		, click = function(node) {
			var event = new MouseEvent("click");
			node.dispatchEvent(event);
		}
		, webkit_req_fs = view.webkitRequestFileSystem
		, req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem
		, throw_outside = function(ex) {
			(view.setImmediate || view.setTimeout)(function() {
				throw ex;
			}, 0);
		}
		, force_saveable_type = "application/octet-stream"
		, fs_min_size = 0
		// See https://code.google.com/p/chromium/issues/detail?id=375297#c7 and
		// https://github.com/eligrey/FileSaver.js/commit/485930a#commitcomment-8768047
		// for the reasoning behind the timeout and revocation flow
		, arbitrary_revoke_timeout = 500 // in ms
		, revoke = function(file) {
			var revoker = function() {
				if (typeof file === "string") { // file is an object URL
					get_URL().revokeObjectURL(file);
				} else { // file is a File
					file.remove();
				}
			};
			if (view.chrome) {
				revoker();
			} else {
				setTimeout(revoker, arbitrary_revoke_timeout);
			}
		}
		, dispatch = function(filesaver, event_types, event) {
			event_types = [].concat(event_types);
			var i = event_types.length;
			while (i--) {
				var listener = filesaver["on" + event_types[i]];
				if (typeof listener === "function") {
					try {
						listener.call(filesaver, event || filesaver);
					} catch (ex) {
						throw_outside(ex);
					}
				}
			}
		}
		, auto_bom = function(blob) {
			// prepend BOM for UTF-8 XML and text/* types (including HTML)
			if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
				return new Blob(["\ufeff", blob], {type: blob.type});
			}
			return blob;
		}
		, FileSaver = function(blob, name, no_auto_bom) {
			if (!no_auto_bom) {
				blob = auto_bom(blob);
			}
			// First try a.download, then web filesystem, then object URLs
			var
				  filesaver = this
				, type = blob.type
				, blob_changed = false
				, object_url
				, target_view
				, dispatch_all = function() {
					dispatch(filesaver, "writestart progress write writeend".split(" "));
				}
				// on any filesys errors revert to saving with object URLs
				, fs_error = function() {
					if (target_view && typeof safari !== "undefined" && typeof FileReader !== "undefined") {
						// Safari doesn't allow downloading of blob urls
						var reader = new FileReader();
						reader.onloadend = function() {
							var base64Data = reader.result;
							base64Data = base64Data.replace(/^data:.*?;/, 'data:attachment/file;');
							target_view.location.href = encodeURI(base64Data);
							filesaver.readyState = filesaver.DONE;
							dispatch_all();
						};
						reader.readAsDataURL(blob);
						filesaver.readyState = filesaver.INIT;
						return;
					}
					// don't create more object URLs than needed
					if (blob_changed || !object_url) {
						object_url = get_URL().createObjectURL(blob);
					}
					if (target_view) {
						target_view.location.href = object_url;
					} else {
						var new_tab = view.open(object_url, "_blank");
						if (new_tab == undefined && typeof safari !== "undefined") {
							//Apple do not allow window.open, see http://bit.ly/1kZffRI
							view.location.href = object_url
						}
					}
					filesaver.readyState = filesaver.DONE;
					dispatch_all();
					revoke(object_url);
				}
				, abortable = function(func) {
					return function() {
						if (filesaver.readyState !== filesaver.DONE) {
							return func.apply(this, arguments);
						}
					};
				}
				, create_if_not_found = {create: true, exclusive: false}
				, slice
			;
			filesaver.readyState = filesaver.INIT;
			if (!name) {
				name = "download";
			}
			if (can_use_save_link) {
				object_url = get_URL().createObjectURL(blob);
				save_link.href = object_url;
				save_link.download = name;
				setTimeout(function() {
					click(save_link);
					dispatch_all();
					revoke(object_url);
					filesaver.readyState = filesaver.DONE;
				});
				return;
			}
			// Object and web filesystem URLs have a problem saving in Google Chrome when
			// viewed in a tab, so I force save with application/octet-stream
			// http://code.google.com/p/chromium/issues/detail?id=91158
			// Update: Google errantly closed 91158, I submitted it again:
			// https://code.google.com/p/chromium/issues/detail?id=389642
			if (view.chrome && type && type !== force_saveable_type) {
				slice = blob.slice || blob.webkitSlice;
				blob = slice.call(blob, 0, blob.size, force_saveable_type);
				blob_changed = true;
			}
			// Since I can't be sure that the guessed media type will trigger a download
			// in WebKit, I append .download to the filename.
			// https://bugs.webkit.org/show_bug.cgi?id=65440
			if (webkit_req_fs && name !== "download") {
				name += ".download";
			}
			if (type === force_saveable_type || webkit_req_fs) {
				target_view = view;
			}
			if (!req_fs) {
				fs_error();
				return;
			}
			fs_min_size += blob.size;
			req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) {
				fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) {
					var save = function() {
						dir.getFile(name, create_if_not_found, abortable(function(file) {
							file.createWriter(abortable(function(writer) {
								writer.onwriteend = function(event) {
									target_view.location.href = file.toURL();
									filesaver.readyState = filesaver.DONE;
									dispatch(filesaver, "writeend", event);
									revoke(file);
								};
								writer.onerror = function() {
									var error = writer.error;
									if (error.code !== error.ABORT_ERR) {
										fs_error();
									}
								};
								"writestart progress write abort".split(" ").forEach(function(event) {
									writer["on" + event] = filesaver["on" + event];
								});
								writer.write(blob);
								filesaver.abort = function() {
									writer.abort();
									filesaver.readyState = filesaver.DONE;
								};
								filesaver.readyState = filesaver.WRITING;
							}), fs_error);
						}), fs_error);
					};
					dir.getFile(name, {create: false}, abortable(function(file) {
						// delete file if it already exists
						file.remove();
						save();
					}), abortable(function(ex) {
						if (ex.code === ex.NOT_FOUND_ERR) {
							save();
						} else {
							fs_error();
						}
					}));
				}), fs_error);
			}), fs_error);
		}
		, FS_proto = FileSaver.prototype
		, saveAs = function(blob, name, no_auto_bom) {
			return new FileSaver(blob, name, no_auto_bom);
		}
	;
	// IE 10+ (native saveAs)
	if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
		return function(blob, name, no_auto_bom) {
			if (!no_auto_bom) {
				blob = auto_bom(blob);
			}
			return navigator.msSaveOrOpenBlob(blob, name || "download");
		};
	}

	FS_proto.abort = function() {
		var filesaver = this;
		filesaver.readyState = filesaver.DONE;
		dispatch(filesaver, "abort");
	};
	FS_proto.readyState = FS_proto.INIT = 0;
	FS_proto.WRITING = 1;
	FS_proto.DONE = 2;

	FS_proto.error =
	FS_proto.onwritestart =
	FS_proto.onprogress =
	FS_proto.onwrite =
	FS_proto.onabort =
	FS_proto.onerror =
	FS_proto.onwriteend =
		null;

	return saveAs;
}(
	   typeof self !== "undefined" && self
	|| typeof window !== "undefined" && window
	|| this.content
));
// `self` is undefined in Firefox for Android content script context
// while `this` is nsIContentFrameMessageManager
// with an attribute `content` that corresponds to the window

if (typeof module !== "undefined" && module.exports) {
  module.exports.saveAs = saveAs;
} else if ((typeof define !== "undefined" && define !== null) && (define.amd != null)) {
  define([], function() {
    return saveAs;
  });
}
<|endoftext|>


<commit_before>import { testAsync } from './testHelpers'
import setupTestApp from './setupTestApp'

testAsync('Add Reference: TODO do serious stuff here', async (t) => {
  let { app } = await setupTestApp(t)
  let articlePanel = app.find('.sc-article-panel')
  // open the manuscript panel
  articlePanel.send('updateViewName', 'metadata')
  // open the add drop down
  let addDropDown = articlePanel.find('.sc-tool-dropdown.sm-add')
  addDropDown.find('.se-toggle').click()
  // click on the add-reference button
  // TODO: implement a different check
  // ATM this click raises an exception, which unfortunately does not yield an exception because the browser consumes exceptions on clicks
  // but there is not result in that case
  let res = addDropDown.find('.sm-add-reference').click()
  // TODO: continue here
  t.ok(Boolean(res))
  t.end()
})
<commit_msg>Fix comment.<commit_after>import { testAsync } from './testHelpers'
import setupTestApp from './setupTestApp'

testAsync('Add Reference: TODO do serious stuff here', async (t) => {
  let { app } = await setupTestApp(t)
  let articlePanel = app.find('.sc-article-panel')
  // open the metadata panel
  articlePanel.send('updateViewName', 'metadata')
  // open the add drop down
  let addDropDown = articlePanel.find('.sc-tool-dropdown.sm-add')
  addDropDown.find('.se-toggle').click()
  // click on the add-reference button
  // TODO: implement a different check
  // ATM this click raises an exception, which unfortunately does not yield an exception because the browser consumes exceptions on clicks
  // but there is not result in that case
  let res = addDropDown.find('.sm-add-reference').click()
  // TODO: continue here
  t.ok(Boolean(res))
  t.end()
})
<|endoftext|>


<commit_before>// @flow
// $FlowIgnoreLine
import { useState, useEffect } from 'react';
import createRandomName from './utils/createRandomName';
import createTag from './style/createTag';
import type { AnimateKeyframesProps } from './animateKeyframes';
import deleteRules from './style/deleteRules';

type UseAnimateKeyframesProps = AnimateKeyframesProps & {
  animationName: string,
};

export default function useAnimateKeyframes(props: UseAnimateKeyframesProps) {
  const {
    durationSeconds = 0.3,
    delaySeconds = 0,
    easeType = 'linear',
    direction = 'normal',
    fillMode = 'none',
    iterationCount = 1,
    playState = 'running',
    keyframes,
  } = props;

  const [animateProps, setPlay] = useState(props);

  const playFunction = (playValue: boolean) => {
    setPlay({
      ...props,
      play: playValue,
    });
  };

  const { play, animationName } = animateProps;

  useEffect(() => {
    const name = createRandomName();
    const { styleTag } = createTag({ animationName: name, keyframes });
    const localStyleTag: any = styleTag;

    setPlay({
      ...props,
      animationName: name,
    });

    return () => {
      deleteRules(localStyleTag.sheet, name);
    };
  }, []);

  const style = play
    ? {
        animation: `${durationSeconds}s ${easeType} ${delaySeconds}s ${iterationCount} ${direction} ${fillMode} ${playState} ${animationName}`,
      }
    : null;

  return [
    {
      style,
      play,
      animationName,
    },
    (playValue: boolean) => playFunction(playValue),
  ];
}
<commit_msg>fix bug with useAnimateKeyframes<commit_after>// @flow
import { useState, useEffect, useRef } from 'react';
import createRandomName from './utils/createRandomName';
import createTag from './style/createTag';
import type { AnimateKeyframesProps } from './animateKeyframes';
import deleteRules from './style/deleteRules';

type UseAnimateKeyframesProps = AnimateKeyframesProps & {
  animationName: string,
};

export default function useAnimateKeyframes(props: UseAnimateKeyframesProps) {
  const {
    durationSeconds = 0.3,
    delaySeconds = 0,
    easeType = 'linear',
    direction = 'normal',
    fillMode = 'none',
    iterationCount = 1,
    playState = 'running',
    keyframes,
  } = props;
  const animationNameRef = useRef('');
  const styleTagRef = useRef(null);
  const [{ play }, setPlay] = useState(props);
  const playFunction = (playValue: boolean) => {
    setPlay({
      ...props,
      play: playValue,
    });
  };

  useEffect(
    () => {
      if (!animationNameRef.current) {
        const name = createRandomName();
        const { styleTag } = createTag({ animationName: name, keyframes });
        styleTagRef.current = styleTag;
        animationNameRef.current = name;
      }

      return () => {
        if (styleTagRef.current && animationNameRef.current) {
          // $FlowIgnoreLine
          deleteRules(styleTagRef.current.sheet, animationNameRef.current);
        }
      };
    },
    [play],
  );

  const style = play
    ? {
        animation: `${durationSeconds}s ${easeType} ${delaySeconds}s ${iterationCount} ${direction} ${fillMode} ${playState} ${animationNameRef.current ||
          ''}`,
      }
    : null;

  return [
    {
      style,
      play,
    },
    (playValue: boolean) => playFunction(playValue),
  ];
}
<|endoftext|>


<commit_before>define(function(require) {

    'use strict';

    var $        = require('jquery');
    var _        = require('underscore');
    var Backbone = require('backbone'); Backbone.$ = $;
    var Pool     = require('object-pool');

    var Vector2 = require('common/math/vector2');

    var vectorPool = Pool({
        init: function() {
            return new Vector2();
        },
        enable: function(vector) {
            vector.set(0, 0);
        }
    });

    var html = require('text!templates/graph.html');

    require('less!styles/graph');

    /**
     *
     */
    var GraphView = Backbone.View.extend({

        className: 'graph-view',

        events: {
            'click .zoom-in-btn'  : 'zoomIn',
            'click .zoom-out-btn' : 'zoomOut'
        },

        paddingLeft: 30,
        paddingRight: 30,
        paddingTop: 6,
        paddingBottom: 34,

        zoomFactor: 0.2,
        minZoom: 0.2,
        maxZoom: 2.6,

        initialize: function(options) {
            // Default values
            options = _.extend({
                title: 'Value',
                points: [],

                x: {
                    start: -8,
                    end: 8,
                    step: 2,
                    label: 'X Axis',
                    showNumbers: true,
                    decimals: undefined
                },
                y: {
                    start: 0,
                    end: 8,
                    step: 2,
                    label: 'Y Axis',
                    showNumbers: true,
                    decimals: undefined
                },

                width: 240,
                height: 124,

                lineThickness: 3,
                lineColor: '#f00',
                lineAlpha: 0.4,
                gridColor: '#ddd',
                gridThickness: 1,
                borderColor: '#000',
                borderThickness: 1,
                axisColor: '#000',
                axisThickness: 2,
                tickLength: 6,
                axisLabelMargin: 4,

                numberFontSize: 10,
                axisLabelFontSize: 14
            }, options);

            this.simulation = options.simulation;

            this.title = options.title;
            this.points = options.points;

            this.x = options.x;
            this.y = options.y;
            this.yEnd = this.y.end; // Because of zooming, the y-end changes
            this.yScale = 1;

            this.elementWidth = options.width;
            this.elementHeight = options.height;

            var resolution = this.getResolution();
            this.width  = options.width  * resolution;
            this.height = options.height * resolution;
            this.paddingLeft   *= resolution;
            this.paddingRight  *= resolution;
            this.paddingTop    *= resolution;
            this.paddingBottom *= resolution;

            this.lineThickness = options.lineThickness * resolution;
            this.lineColor = options.lineColor;
            this.lineAlpha = options.lineAlpha;
            this.gridColor = options.gridColor;
            this.gridThickness = options.gridThickness * resolution;
            this.borderColor = options.borderColor;
            this.borderThickness = options.borderThickness * resolution;
            this.axisColor = options.axisColor;
            this.axisThickness = options.axisThickness * resolution;
            this.tickLength = options.tickLength * resolution;
            this.axisLabelMargin = options.axisLabelMargin;

            this.numberFont = options.numberFontSize * resolution + 'px Helvetica Neue';
            this.axisLabelFont = options.axisLabelFontSize * resolution + 'px Helvetica Neue';
        },

        /**
         * Renders the view
         */
        render: function() {
            this.$el.html(html);
            return this;
        },

        /**
         * Sizes the canvas and initializes the canvas context
         */
        postRender: function() {
            // Give the canvas its dimensions
            var $canvas = this.$('canvas');
            $canvas.width(this.elementWidth);
            $canvas.height(this.elementHeight);
            var canvas = this.$('canvas')[0];
            canvas.width = this.width;
            canvas.height = this.height;

            // Get the canvas context
            this.ctx = canvas.getContext('2d');

            return this;
        },

        /**
         * Updates the graph
         */
        update: function() {
            this.draw();
        },

        /**
         * Draws the graph
         */
        draw: function() {
            this.ctx.clearRect(0, 0, this.width, this.height);

            this.drawEmptyGraph();
            this.drawData();
        },

        drawEmptyGraph: function() {
            var ctx = this.ctx;
            var width = this.getGraphWidth();
            var height = this.getGraphHeight();
            var originX = this.paddingLeft;
            var originY = this.paddingTop + height;

            var cols = (this.x.end - this.x.start) / this.x.step;
            var gridCellWidth  = width / cols;
            var gridCellHeight = this.getRowHeight();
            var xZeroOffset = (0 - this.x.start) * (width / (this.x.end - this.x.start));

            var c;
            var y;
            var n;

            // Draw background
            ctx.fillStyle = '#fff';
            ctx.fillRect(originX, originY - height, width, height);

            // Draw Grid
            ctx.beginPath();

            // Draw longitudinal grid lines
            for (c = 0; c <= cols; c++) {
                ctx.moveTo(originX + gridCellWidth * c, originY);
                ctx.lineTo(originX + gridCellWidth * c, originY - height);
            }

            // Draw latitudinal grid lines and numbers
            for (y = originY; y >= originY - height; y -= gridCellHeight) {
                ctx.moveTo(originX,         y);
                ctx.lineTo(originX + width, y);
            }

            ctx.lineWidth = this.gridThickness;
            ctx.strokeStyle = this.gridColor;
            ctx.stroke();

            // Draw axis lines
            ctx.beginPath();
            ctx.moveTo(originX, originY);
            ctx.lineTo(originX + width, originY);

            // Draw the y-axis line at x = 0
            ctx.moveTo(originX + xZeroOffset, originY - height);
            ctx.lineTo(originX + xZeroOffset, originY);

            ctx.lineWidth = this.axisThickness;
            ctx.strokeStyle = this.axisColor;
            ctx.stroke();

            // Draw ticks
            var halfTick = this.tickLength / 2;

            if (this.x.showNumbers) {
                for (c = 0; c <= cols; c++) {
                    ctx.moveTo(originX + gridCellWidth * c, originY + halfTick);
                    ctx.lineTo(originX + gridCellWidth * c, originY - halfTick);
                }
            }

            if (this.y.showNumbers) {
                var startY = this.y.start;
                var stepY = this.y.step;
                for (y = originY; y >= originY - height; y -= gridCellHeight) {
                    ctx.moveTo(originX + xZeroOffset - halfTick,   y);
                    ctx.lineTo(originX + xZeroOffset + halfTick, y);
                }
            }

            ctx.lineWidth = this.axisThickness;
            ctx.strokeStyle = this.axisColor;
            ctx.stroke();

            // Draw numbers
            ctx.font = this.numberFont;
            ctx.fillStyle = this.axisColor;

            if (this.x.showNumbers) {
                ctx.textAlign = 'center';
                ctx.textBaseline = 'top';
                var startX = this.x.start;
                var stepX = this.x.step;

                for (c = 0; c <= cols; c++) {
                    n = startX + (c * stepX);
                    if (this.x.decimals !== undefined)
                        n = n.toFixed(this.x.decimals);
                    ctx.fillText(n, originX + gridCellWidth * c, originY + halfTick);
                }
            }

            if (this.y.showNumbers) {
                ctx.textAlign = 'right';
                ctx.textBaseline = 'middle';
                var stepY = this.y.step;
                var yValue = this.y.start;

                for (y = originY; y >= originY - height; y -= gridCellHeight) {
                    n = yValue;
                    if (this.y.decimals !== undefined)
                        n = n.toFixed(this.y.decimals);
                    ctx.fillText(n, originX - halfTick, y);
                    yValue += stepY;
                }
            }

            // Draw axis labels
            ctx.font = this.axisLabelFont;
            ctx.textAlign = 'center';
            ctx.textBaseline = 'bottom';

            ctx.fillText(this.x.label, originX + width / 2, this.height - this.axisLabelMargin);

            var yOffset = originY - height / 2;
            ctx.textBaseline = 'top';
            ctx.rotate(-Math.PI / 2);
            ctx.fillText(this.y.label, -yOffset, this.axisLabelMargin);
            ctx.rotate(Math.PI / 2);

            // Draw border
            ctx.beginPath();
            ctx.rect(this.paddingLeft, this.paddingTop, width, height);
            ctx.lineWidth = this.borderThickness;
            ctx.strokeStyle = this.borderColor;
            ctx.stroke();
        },

        drawData: function() {
            if (this.points.length === 0)
                return;

            // Draw the data points
            var ctx = this.ctx;
            var points = this.points;
            var width = this.getGraphWidth();
            var height = this.getGraphHeight();
            var originX = this.paddingLeft;
            var originY = this.paddingTop + height;
            var xToPx = this.getPixelsPerUnitX();
            var yToPx = this.getPixelsPerUnitY();
            var xZeroOffset = (0 - this.x.start) * (width / (this.x.end - this.x.start));

            ctx.beginPath();
            ctx.moveTo(originX + points[0].x * xToPx + xZeroOffset, originY - points[0].y * yToPx);

            for (i = 1; i < this.points.length; i++)
                ctx.lineTo(originX + points[i].x * xToPx + xZeroOffset, originY - points[i].y * yToPx);

            ctx.lineWidth = this.lineThickness;
            ctx.lineJoin = 'round';
            ctx.globalAlpha = this.lineAlpha;
            ctx.strokeStyle = this.lineColor;
            ctx.stroke();
            ctx.globalAlpha = 1;

            // Draw a circle on the current data point
            var currentPoint = this.points[this.points.length - 1];
            var radius = this.lineThickness;
            ctx.beginPath();
            ctx.arc(originX + currentPoint.x * xToPx + xZeroOffset, originY - currentPoint.y * yToPx, radius, 0, 2 * Math.PI);
            ctx.fillStyle = this.lineColor;
            ctx.fill();
        },

        createPoint: function(x, y) {
            return vectorPool.create().set(x, y);
        },

        destroyPoint: function(point) {
            vectorPool.remove(point);
        },

        clearPoints: function() {
            for (var i = this.points.length - 1; i >= 0; i--) {
                vectorPool.remove(this.points[i]);
                this.points.splice(i, 1);
            }
        },

        getGraphWidth: function() {
            return this.width - this.paddingLeft - this.paddingRight;
        },

        getGraphHeight: function() {
            return this.height - this.paddingTop - this.paddingBottom;
        },

        getPixelsPerUnitX: function() {
            return this.getGraphWidth() / (this.x.end - this.x.start);
        },

        getPixelsPerUnitY: function() {
            var yEnd = this.y.end * this.yScale;
            var pixelsPerUnit = this.getGraphHeight() / yEnd;
            return pixelsPerUnit;
        },

        /**
         * Calculates and returns the height of each grid row based on the zoom
         *   level and the y-axis step size.
         */
        getRowHeight: function() {
            return Math.round(this.getPixelsPerUnitY()) * this.y.step;
        },

        getResolution: function() {
            return window.devicePixelRatio ? window.devicePixelRatio : 1;
        },

        zoomIn: function() {
            var zoom = this.yScale + this.zoomFactor;
            if (zoom <= this.maxZoom) {
                this.yScale = zoom;
                this.draw();
            }
        },

        zoomOut: function() {
            var zoom = this.yScale - this.zoomFactor;
            if (zoom >= this.minZoom) {
                this.yScale = zoom;
                this.draw();
            }
        }

    });

    return GraphView;
});
<commit_msg>Made the axis labels centered in their space when no axis ticks or numbers are present.<commit_after>define(function(require) {

    'use strict';

    var $        = require('jquery');
    var _        = require('underscore');
    var Backbone = require('backbone'); Backbone.$ = $;
    var Pool     = require('object-pool');

    var Vector2 = require('common/math/vector2');

    var vectorPool = Pool({
        init: function() {
            return new Vector2();
        },
        enable: function(vector) {
            vector.set(0, 0);
        }
    });

    var html = require('text!templates/graph.html');

    require('less!styles/graph');

    /**
     *
     */
    var GraphView = Backbone.View.extend({

        className: 'graph-view',

        events: {
            'click .zoom-in-btn'  : 'zoomIn',
            'click .zoom-out-btn' : 'zoomOut'
        },

        paddingLeft: 30,
        paddingRight: 30,
        paddingTop: 6,
        paddingBottom: 34,

        zoomFactor: 0.2,
        minZoom: 0.2,
        maxZoom: 2.6,

        initialize: function(options) {
            // Default values
            options = _.extend({
                title: 'Value',
                points: [],

                x: {
                    start: -8,
                    end: 8,
                    step: 2,
                    label: 'X Axis',
                    showNumbers: true,
                    decimals: undefined
                },
                y: {
                    start: 0,
                    end: 8,
                    step: 2,
                    label: 'Y Axis',
                    showNumbers: true,
                    decimals: undefined
                },

                width: 240,
                height: 124,

                lineThickness: 3,
                lineColor: '#f00',
                lineAlpha: 0.4,
                gridColor: '#ddd',
                gridThickness: 1,
                borderColor: '#000',
                borderThickness: 1,
                axisColor: '#000',
                axisThickness: 2,
                tickLength: 6,
                axisLabelMargin: 4,

                numberFontSize: 10,
                axisLabelFontSize: 14
            }, options);

            this.simulation = options.simulation;

            this.title = options.title;
            this.points = options.points;

            this.x = options.x;
            this.y = options.y;
            this.yEnd = this.y.end; // Because of zooming, the y-end changes
            this.yScale = 1;

            this.elementWidth = options.width;
            this.elementHeight = options.height;

            var resolution = this.getResolution();
            this.width  = options.width  * resolution;
            this.height = options.height * resolution;
            this.paddingLeft   *= resolution;
            this.paddingRight  *= resolution;
            this.paddingTop    *= resolution;
            this.paddingBottom *= resolution;

            this.lineThickness = options.lineThickness * resolution;
            this.lineColor = options.lineColor;
            this.lineAlpha = options.lineAlpha;
            this.gridColor = options.gridColor;
            this.gridThickness = options.gridThickness * resolution;
            this.borderColor = options.borderColor;
            this.borderThickness = options.borderThickness * resolution;
            this.axisColor = options.axisColor;
            this.axisThickness = options.axisThickness * resolution;
            this.tickLength = options.tickLength * resolution;
            this.axisLabelMargin = options.axisLabelMargin;

            this.numberFont = options.numberFontSize * resolution + 'px Helvetica Neue';
            this.axisLabelFont = options.axisLabelFontSize * resolution + 'px Helvetica Neue';
        },

        /**
         * Renders the view
         */
        render: function() {
            this.$el.html(html);
            return this;
        },

        /**
         * Sizes the canvas and initializes the canvas context
         */
        postRender: function() {
            // Give the canvas its dimensions
            var $canvas = this.$('canvas');
            $canvas.width(this.elementWidth);
            $canvas.height(this.elementHeight);
            var canvas = this.$('canvas')[0];
            canvas.width = this.width;
            canvas.height = this.height;

            // Get the canvas context
            this.ctx = canvas.getContext('2d');

            return this;
        },

        /**
         * Updates the graph
         */
        update: function() {
            this.draw();
        },

        /**
         * Draws the graph
         */
        draw: function() {
            this.ctx.clearRect(0, 0, this.width, this.height);

            this.drawEmptyGraph();
            this.drawData();
        },

        drawEmptyGraph: function() {
            var ctx = this.ctx;
            var width = this.getGraphWidth();
            var height = this.getGraphHeight();
            var originX = this.paddingLeft;
            var originY = this.paddingTop + height;

            var cols = (this.x.end - this.x.start) / this.x.step;
            var gridCellWidth  = width / cols;
            var gridCellHeight = this.getRowHeight();
            var xZeroOffset = (0 - this.x.start) * (width / (this.x.end - this.x.start));

            var c;
            var y;
            var n;

            // Draw background
            ctx.fillStyle = '#fff';
            ctx.fillRect(originX, originY - height, width, height);

            // Draw Grid
            ctx.beginPath();

            // Draw longitudinal grid lines
            for (c = 0; c <= cols; c++) {
                ctx.moveTo(originX + gridCellWidth * c, originY);
                ctx.lineTo(originX + gridCellWidth * c, originY - height);
            }

            // Draw latitudinal grid lines and numbers
            for (y = originY; y >= originY - height; y -= gridCellHeight) {
                ctx.moveTo(originX,         y);
                ctx.lineTo(originX + width, y);
            }

            ctx.lineWidth = this.gridThickness;
            ctx.strokeStyle = this.gridColor;
            ctx.stroke();

            // Draw axis lines
            ctx.beginPath();
            ctx.moveTo(originX, originY);
            ctx.lineTo(originX + width, originY);

            // Draw the y-axis line at x = 0
            ctx.moveTo(originX + xZeroOffset, originY - height);
            ctx.lineTo(originX + xZeroOffset, originY);

            ctx.lineWidth = this.axisThickness;
            ctx.strokeStyle = this.axisColor;
            ctx.stroke();

            // Draw ticks
            var halfTick = this.tickLength / 2;

            if (this.x.showNumbers) {
                for (c = 0; c <= cols; c++) {
                    ctx.moveTo(originX + gridCellWidth * c, originY + halfTick);
                    ctx.lineTo(originX + gridCellWidth * c, originY - halfTick);
                }
            }

            if (this.y.showNumbers) {
                var startY = this.y.start;
                var stepY = this.y.step;
                for (y = originY; y >= originY - height; y -= gridCellHeight) {
                    ctx.moveTo(originX + xZeroOffset - halfTick,   y);
                    ctx.lineTo(originX + xZeroOffset + halfTick, y);
                }
            }

            ctx.lineWidth = this.axisThickness;
            ctx.strokeStyle = this.axisColor;
            ctx.stroke();

            // Draw numbers
            ctx.font = this.numberFont;
            ctx.fillStyle = this.axisColor;

            if (this.x.showNumbers) {
                ctx.textAlign = 'center';
                ctx.textBaseline = 'top';
                var startX = this.x.start;
                var stepX = this.x.step;

                for (c = 0; c <= cols; c++) {
                    n = startX + (c * stepX);
                    if (this.x.decimals !== undefined)
                        n = n.toFixed(this.x.decimals);
                    ctx.fillText(n, originX + gridCellWidth * c, originY + halfTick);
                }
            }

            if (this.y.showNumbers) {
                ctx.textAlign = 'right';
                ctx.textBaseline = 'middle';
                var stepY = this.y.step;
                var yValue = this.y.start;

                for (y = originY; y >= originY - height; y -= gridCellHeight) {
                    n = yValue;
                    if (this.y.decimals !== undefined)
                        n = n.toFixed(this.y.decimals);
                    ctx.fillText(n, originX - halfTick, y);
                    yValue += stepY;
                }
            }

            // Draw axis labels
            ctx.font = this.axisLabelFont;
            ctx.textAlign = 'center';

            var xOffset = originX + width / 2;
            var yOffset = this.height - this.axisLabelMargin;
            if (!this.x.showNumbers)
                yOffset -= 12;
            ctx.textBaseline = 'bottom';
            ctx.fillText(this.x.label, xOffset, yOffset);

            yOffset = originY - height / 2;
            xOffset = this.axisLabelMargin;
            if (!this.y.showNumbers)
                xOffset += 8;
            ctx.textBaseline = 'top';
            ctx.rotate(-Math.PI / 2);
            ctx.fillText(this.y.label, -yOffset, xOffset);
            ctx.rotate(Math.PI / 2);

            // Draw border
            ctx.beginPath();
            ctx.rect(this.paddingLeft, this.paddingTop, width, height);
            ctx.lineWidth = this.borderThickness;
            ctx.strokeStyle = this.borderColor;
            ctx.stroke();
        },

        drawData: function() {
            if (this.points.length === 0)
                return;

            // Draw the data points
            var ctx = this.ctx;
            var points = this.points;
            var width = this.getGraphWidth();
            var height = this.getGraphHeight();
            var originX = this.paddingLeft;
            var originY = this.paddingTop + height;
            var xToPx = this.getPixelsPerUnitX();
            var yToPx = this.getPixelsPerUnitY();
            var xZeroOffset = (0 - this.x.start) * (width / (this.x.end - this.x.start));

            ctx.beginPath();
            ctx.moveTo(originX + points[0].x * xToPx + xZeroOffset, originY - points[0].y * yToPx);

            for (i = 1; i < this.points.length; i++)
                ctx.lineTo(originX + points[i].x * xToPx + xZeroOffset, originY - points[i].y * yToPx);

            ctx.lineWidth = this.lineThickness;
            ctx.lineJoin = 'round';
            ctx.globalAlpha = this.lineAlpha;
            ctx.strokeStyle = this.lineColor;
            ctx.stroke();
            ctx.globalAlpha = 1;

            // Draw a circle on the current data point
            var currentPoint = this.points[this.points.length - 1];
            var radius = this.lineThickness;
            ctx.beginPath();
            ctx.arc(originX + currentPoint.x * xToPx + xZeroOffset, originY - currentPoint.y * yToPx, radius, 0, 2 * Math.PI);
            ctx.fillStyle = this.lineColor;
            ctx.fill();
        },

        createPoint: function(x, y) {
            return vectorPool.create().set(x, y);
        },

        destroyPoint: function(point) {
            vectorPool.remove(point);
        },

        clearPoints: function() {
            for (var i = this.points.length - 1; i >= 0; i--) {
                vectorPool.remove(this.points[i]);
                this.points.splice(i, 1);
            }
        },

        getGraphWidth: function() {
            return this.width - this.paddingLeft - this.paddingRight;
        },

        getGraphHeight: function() {
            return this.height - this.paddingTop - this.paddingBottom;
        },

        getPixelsPerUnitX: function() {
            return this.getGraphWidth() / (this.x.end - this.x.start);
        },

        getPixelsPerUnitY: function() {
            var yEnd = this.y.end * this.yScale;
            var pixelsPerUnit = this.getGraphHeight() / yEnd;
            return pixelsPerUnit;
        },

        /**
         * Calculates and returns the height of each grid row based on the zoom
         *   level and the y-axis step size.
         */
        getRowHeight: function() {
            return Math.round(this.getPixelsPerUnitY()) * this.y.step;
        },

        getResolution: function() {
            return window.devicePixelRatio ? window.devicePixelRatio : 1;
        },

        zoomIn: function() {
            var zoom = this.yScale + this.zoomFactor;
            if (zoom <= this.maxZoom) {
                this.yScale = zoom;
                this.draw();
            }
        },

        zoomOut: function() {
            var zoom = this.yScale - this.zoomFactor;
            if (zoom >= this.minZoom) {
                this.yScale = zoom;
                this.draw();
            }
        }

    });

    return GraphView;
});
<|endoftext|>


<commit_before>'use strict';
var logger = require('../logger'),
  async = require('async'),
  crypto = require('crypto'),
  nodemailer = require('nodemailer'),
  util = require('util'),
  utils = require('../utils.js'),
  config = require('config'),
  addTags = function (req, tag1, tag2, tag3, user, cb) {

    var tagAdd = function (tag, done) {
      req.app.get('database').model.Tag.findOrCreate({ where: {"name": tag.name}}).success(function (item, created) {
        if (created) {
          item.count = tag.value;
          item.save().success(function () {
            done(null, {tag: item, value: tag.value});
          }).failure(function (err) { cb(err); });
        } else {
          item.increment('count', { by: tag.value}).success(function () {
            done(null, {tag: item, value: tag.value});
          }).failure(function (err) { cb(err); });
        }
      }).failure(function (err) { cb(err); });
    };

    async.mapSeries([{name: tag1, value: 30}, {name: tag2, value: 20}, {name: tag3, value: 10}], tagAdd, function (err, results) {
      async.eachSeries(results, function (item, done) {
        user.hasTag(item.tag).then(function (ret) {
          if (!ret) {
            return user.addTag(item.tag, {value: item.value}).then(function () {return done(); });
          }
          req.app.get('database').model.TagsUsers.find({where: { $and: [{TagId: item.tag.id}, {UserId: user.id}]}}).then(function (rel) {
            rel.increment('value', { by: item.value }).then(function () {
              return done();
            });
          });
        });
      });
      cb(err);
    });
  },
  welcomeEmail = function (to, token, done) {

    var options = {},
      smtpTransport,
      body;
    options.host = config.email.smtpHost;
    options.secureConnection = config.email.secureConnection;
    options.port = config.email.port;
    if (config.email.auth) {
      options.auth = config.email.auth;
    }

    smtpTransport = nodemailer.createTransport("SMTP", options);

    body = 'Du hast dich erfolgreich zum BarCamp Graz 2015 angemeldet.\n' +
      'Um dich wieder abzumelden verwende bitte folgenden Link:\n\n' +
      'http://' + config.general.host  + '/areyousure/' + token + '\n\n';

    smtpTransport.sendMail({
      from: config.email.sender,
      to: to,
      subject: config.email.subjectWelcome,
      text: body
    }, function (error, response) {
      if (error) {
        console.log(error);
        smtpTransport.close();
        done(error);
      } else {
        logger.info("Message sent: " + response.message);
        smtpTransport.close();
        done(null);
      }
    });
  },
  cancelEmail = function (to, token, done) {

    var options = {},
      smtpTransport,
      body;
    options.host = config.email.smtpHost;
    options.secureConnection = config.email.secureConnection;
    options.port = config.email.port;
    if (config.email.auth) {
      options.auth = config.email.auth;
    }

    smtpTransport = nodemailer.createTransport("SMTP", options);

    body = 'Du möchtest dich vom BarCamp Graz 2015 abmelden.\n' +
      'Um dich abzumelden verwende bitte folgenden Link:\n\n' +
      'http://' + config.general.host + '/areyousure/' + token + '\n\n';

    smtpTransport.sendMail({
      from: config.email.sender,
      to: to,
      subject: config.email.subjectGoodbye,
      text: body
    }, function (error, response) {
      if (error) {
        console.log(error);
        smtpTransport.close();
        done(error);
      } else {
        logger.info("Message sent: " + response.message);
        smtpTransport.close();
        done(null);
      }
    });
  };

exports.add = function (req, res, next) {

  req.assert('tag1', 'Bitte überprüfe deine Eingabe').notEmpty().tag();
  req.assert('tag2', 'Bitte überprüfe deine Eingabe').notEmpty().tag();
  req.assert('tag3', 'Bitte überprüfe deine Eingabe').notEmpty().tag();

  if (req.validationErrors()) {
    next(new Error('Malformed input'));
  }

  addTags(req, req.body.tag1, req.body.tag2, req.body.tag3, null, function (err) {
    if (err) { next(err); }
    res.render('form');
  });
};

exports.doRegister = function (req, res, next) {

  if (utils.developmentEnvironment()) {
    console.log(req.body);
  }

  req.assert('firstName', 'Bitte überprüfe deine Eingabe').notEmpty().validName().isLength(3, 36);
  req.assert('lastName', 'Bitte überprüfe deine Eingabe').optional().validName().isLength(3, 36);
  req.assert('email', 'Bitte überprüfe deine Eingabe').notEmpty().isEmail().isLength(3, 128);
  req.assert('tag1', 'Bitte überprüfe deine Eingabe').notEmpty().tag().isLength(2, 36);
  req.assert('tag2', 'Bitte überprüfe deine Eingabe').notEmpty().tag().isLength(2, 36);
  req.assert('tag3', 'Bitte überprüfe deine Eingabe').notEmpty().tag().isLength(2, 36);
  req.assert('preferredCamp').optional().isIn(['uxcamp', 'politcamp', 'startcamp', 'designcamp', 'appdevcamp', 'wissenscamp']);
  req.assert('gender').optional().isIn(['m', 'w', 'none']);
  req.assert('shirtSize').optional().isIn(['S', 'M', 'L', 'XL', 'none']);
  req.assert('note', 'Bitte überprüfe deine Eingabe').optional().isLength(0, 768);
  if (req.body.note) {
    req.assert('note', 'Bitte überprüfe deine Eingabe').optional().isAscii();
  }
  req.assert('children', 'Bitte überprüfe deine Eingabe').optional().equals('true');
  req.assert('newcomer', 'Bitte überprüfe deine Eingabe').optional().equals('true');

  req.assert('session_title').optional().isLength(0, 256);
  if (req.body.session_title) {
    req.assert('session_title', 'Bitte überprüfe deine Eingabe').optional().isAscii();
  }
  req.assert('session_desc').optional().isLength(0, 768);
  if (req.body.session_desc) {
    req.assert('session_desc', 'Bitte überprüfe deine Eingabe').optional().isAscii();
  }
  req.assert('day_one').optional().equals('true');
  req.assert('day_two').optional().equals('true');
  req.assert('day_three').optional().equals('true');
  req.assert('twitter').optional().twitterUsername();
  req.assert('facebook').optional().facebookUsername();
  req.assert('google_plus').optional().isAscii().isLength(0, 256);
  req.assert('website').optional().isURL().isLength(0, 256);
  req.assert('accept').equals('true');

  var errors = req.validationErrors();

  if (errors) {
    next(new Error('Danke, aber wir konnten deine Eingabe nicht annehmen.' + util.inspect(errors)));
    return;
  }

  req.sanitize('email').normalizeEmail();

  req.app.get('database').model.User.findAll({ where: {email: req.body.email}}).success(function (users) {

    if (users.length > 0) {
      next(new Error('Die Email Adresse wurde bereits für eine Anmeldung verwendet. Bitte verwende eine andere Adresse oder melde dich vor erneutem Anmelden mit dem Link in der Bestätigungs-Email ab.'));
    } else {

      crypto.randomBytes(20, function (err, buf) {
        if (err) { next(err); }
        var token = buf.toString('hex');

        req.app.get('database').model.User.create({

          firstName: req.body.firstName,
          lastName: req.body.lastName,
          email: req.body.email,
          token: token,
          preferredCamp: req.body.preferredCamp,
          shirtSize: req.body.shirtSize,
          note: req.body.note,
          children: req.body.children,
          newcomer: req.body.newcomer,
          session_title: req.body.session_title,
          session_desc: req.body.session_desc,
          day_one: req.body.day_one,
          day_two: req.body.day_two,
          day_three: req.body.day_three,
          twitter: req.body.twitter,
          facebook: req.body.facebook,
          google_plus: req.body.google_plus,
          website: req.body.website,
          gender: req.body.gender

        }).success(function (user) {

          addTags(req, req.body.tag1, req.body.tag2, req.body.tag3, user, function (err) {
            if (err) { next(err); }

            welcomeEmail(req.body.email, token, function () {
              res.render('thankyou');
            });

          });

        }).error(next);
      });
    }
  });
};

exports.cancelRegistration = function (req, res, next) {

  var removeTags = function (user, cb) {
    //user.getTags().then(function (tags) {
    req.app.get('database').model.TagsUsers.findAll({where: {UserId: user.id}}).then(function (rels) {

      async.each(rels, function (rel, done) {
        var vvv = rel.value;
        req.app.get('database').model.Tag.find({where: {id: rel.TagId}}).then(function (tag) {
          tag.decrement('count', { by: vvv}).then(function () {
            done();
          });
        });
      }, function () {
        //req.app.get('database').model.Tag.findAll({where: {count: 0}}).then(function () {
        req.app.get('database').model.TagsUsers.destroy({where: {UserId: user.id}}).then(function () {
          cb();
        });
        //});
      });
    });
  };

  req.assert('token', 'Invalid Input.').notEmpty().isHexadecimal();

  if (req.validationErrors()) {
    next(new Error('Danke, aber wir konnten deine Eingabe nicht annehmen.'));
  }

  req.app.get('database').model.User.find({ where: { token: req.params.token } }).success(function (user) {
    if (!user) {
      next(new Error('Abmeldungs-Token ist ungültig oder abgelaufen.'));
    }

    removeTags(user, function () {
      user.destroy().success(function () {
        req.app.get('database').sequelize.query("DELETE FROM Tags where count = 0").then(function () {
          res.render('cancel');
        });
      });
    });
  });

};

exports.sendCancelMail = function (req, res, next) {

  req.assert('cancel_email', 'Invalid Input.').notEmpty().isEmail();

  if (req.validationErrors()) {
    next(new Error('Danke, aber wir konnten deine Eingabe nicht annehmen.'));
  }

  var cancel_email = req.body.cancel_email,
    user =  req.app.get('database').model.User.find({ where: {email: cancel_email}}),
    token = user.token;

  cancelEmail(cancel_email, token, function () {
    res.render('cancelemailsent');
  });

};
<commit_msg>removed isAscii validation<commit_after>'use strict';
var logger = require('../logger'),
  async = require('async'),
  crypto = require('crypto'),
  nodemailer = require('nodemailer'),
  util = require('util'),
  utils = require('../utils.js'),
  config = require('config'),
  addTags = function (req, tag1, tag2, tag3, user, cb) {

    var tagAdd = function (tag, done) {
      req.app.get('database').model.Tag.findOrCreate({ where: {"name": tag.name}}).success(function (item, created) {
        if (created) {
          item.count = tag.value;
          item.save().success(function () {
            done(null, {tag: item, value: tag.value});
          }).failure(function (err) { cb(err); });
        } else {
          item.increment('count', { by: tag.value}).success(function () {
            done(null, {tag: item, value: tag.value});
          }).failure(function (err) { cb(err); });
        }
      }).failure(function (err) { cb(err); });
    };

    async.mapSeries([{name: tag1, value: 30}, {name: tag2, value: 20}, {name: tag3, value: 10}], tagAdd, function (err, results) {
      async.eachSeries(results, function (item, done) {
        user.hasTag(item.tag).then(function (ret) {
          if (!ret) {
            return user.addTag(item.tag, {value: item.value}).then(function () {return done(); });
          }
          req.app.get('database').model.TagsUsers.find({where: { $and: [{TagId: item.tag.id}, {UserId: user.id}]}}).then(function (rel) {
            rel.increment('value', { by: item.value }).then(function () {
              return done();
            });
          });
        });
      });
      cb(err);
    });
  },
  welcomeEmail = function (to, token, done) {

    var options = {},
      smtpTransport,
      body;
    options.host = config.email.smtpHost;
    options.secureConnection = config.email.secureConnection;
    options.port = config.email.port;
    if (config.email.auth) {
      options.auth = config.email.auth;
    }

    smtpTransport = nodemailer.createTransport("SMTP", options);

    body = 'Du hast dich erfolgreich zum BarCamp Graz 2015 angemeldet.\n' +
      'Um dich wieder abzumelden verwende bitte folgenden Link:\n\n' +
      'http://' + config.general.host  + '/areyousure/' + token + '\n\n';

    smtpTransport.sendMail({
      from: config.email.sender,
      to: to,
      subject: config.email.subjectWelcome,
      text: body
    }, function (error, response) {
      if (error) {
        console.log(error);
        smtpTransport.close();
        done(error);
      } else {
        logger.info("Message sent: " + response.message);
        smtpTransport.close();
        done(null);
      }
    });
  },
  cancelEmail = function (to, token, done) {

    var options = {},
      smtpTransport,
      body;
    options.host = config.email.smtpHost;
    options.secureConnection = config.email.secureConnection;
    options.port = config.email.port;
    if (config.email.auth) {
      options.auth = config.email.auth;
    }

    smtpTransport = nodemailer.createTransport("SMTP", options);

    body = 'Du möchtest dich vom BarCamp Graz 2015 abmelden.\n' +
      'Um dich abzumelden verwende bitte folgenden Link:\n\n' +
      'http://' + config.general.host + '/areyousure/' + token + '\n\n';

    smtpTransport.sendMail({
      from: config.email.sender,
      to: to,
      subject: config.email.subjectGoodbye,
      text: body
    }, function (error, response) {
      if (error) {
        console.log(error);
        smtpTransport.close();
        done(error);
      } else {
        logger.info("Message sent: " + response.message);
        smtpTransport.close();
        done(null);
      }
    });
  };

exports.add = function (req, res, next) {

  req.assert('tag1', 'Bitte überprüfe deine Eingabe').notEmpty().tag();
  req.assert('tag2', 'Bitte überprüfe deine Eingabe').notEmpty().tag();
  req.assert('tag3', 'Bitte überprüfe deine Eingabe').notEmpty().tag();

  if (req.validationErrors()) {
    next(new Error('Malformed input'));
  }

  addTags(req, req.body.tag1, req.body.tag2, req.body.tag3, null, function (err) {
    if (err) { next(err); }
    res.render('form');
  });
};

exports.doRegister = function (req, res, next) {

  if (utils.developmentEnvironment()) {
    console.log(req.body);
  }

  req.assert('firstName', 'Bitte überprüfe deine Eingabe').notEmpty().validName().isLength(3, 36);
  req.assert('lastName', 'Bitte überprüfe deine Eingabe').optional().validName().isLength(3, 36);
  req.assert('email', 'Bitte überprüfe deine Eingabe').notEmpty().isEmail().isLength(3, 128);
  req.assert('tag1', 'Bitte überprüfe deine Eingabe').notEmpty().tag().isLength(2, 36);
  req.assert('tag2', 'Bitte überprüfe deine Eingabe').notEmpty().tag().isLength(2, 36);
  req.assert('tag3', 'Bitte überprüfe deine Eingabe').notEmpty().tag().isLength(2, 36);
  req.assert('preferredCamp').optional().isIn(['uxcamp', 'politcamp', 'startcamp', 'designcamp', 'appdevcamp', 'wissenscamp']);
  req.assert('gender').optional().isIn(['m', 'w', 'none']);
  req.assert('shirtSize').optional().isIn(['S', 'M', 'L', 'XL', 'none']);
  req.assert('note', 'Bitte überprüfe deine Eingabe').optional().isLength(0, 768);
  if (req.body.note) {
    req.assert('note', 'Bitte überprüfe deine Eingabe').optional();
  }
  req.assert('children', 'Bitte überprüfe deine Eingabe').optional().equals('true');
  req.assert('newcomer', 'Bitte überprüfe deine Eingabe').optional().equals('true');

  req.assert('session_title').optional().isLength(0, 256);
  if (req.body.session_title) {
    req.assert('session_title', 'Bitte überprüfe deine Eingabe').optional();
  }
  req.assert('session_desc').optional().isLength(0, 768);
  if (req.body.session_desc) {
    req.assert('session_desc', 'Bitte überprüfe deine Eingabe').optional();
  }
  req.assert('day_one').optional().equals('true');
  req.assert('day_two').optional().equals('true');
  req.assert('day_three').optional().equals('true');
  req.assert('twitter').optional().twitterUsername();
  req.assert('facebook').optional().facebookUsername();
  req.assert('google_plus').optional().isAscii().isLength(0, 256);
  req.assert('website').optional().isURL().isLength(0, 256);
  req.assert('accept').equals('true');

  var errors = req.validationErrors();

  if (errors) {
    next(new Error('Danke, aber wir konnten deine Eingabe nicht annehmen.' + util.inspect(errors)));
    return;
  }

  req.sanitize('email').normalizeEmail();

  req.app.get('database').model.User.findAll({ where: {email: req.body.email}}).success(function (users) {

    if (users.length > 0) {
      next(new Error('Die Email Adresse wurde bereits für eine Anmeldung verwendet. Bitte verwende eine andere Adresse oder melde dich vor erneutem Anmelden mit dem Link in der Bestätigungs-Email ab.'));
    } else {

      crypto.randomBytes(20, function (err, buf) {
        if (err) { next(err); }
        var token = buf.toString('hex');

        req.app.get('database').model.User.create({

          firstName: req.body.firstName,
          lastName: req.body.lastName,
          email: req.body.email,
          token: token,
          preferredCamp: req.body.preferredCamp,
          shirtSize: req.body.shirtSize,
          note: req.body.note,
          children: req.body.children,
          newcomer: req.body.newcomer,
          session_title: req.body.session_title,
          session_desc: req.body.session_desc,
          day_one: req.body.day_one,
          day_two: req.body.day_two,
          day_three: req.body.day_three,
          twitter: req.body.twitter,
          facebook: req.body.facebook,
          google_plus: req.body.google_plus,
          website: req.body.website,
          gender: req.body.gender

        }).success(function (user) {

          addTags(req, req.body.tag1, req.body.tag2, req.body.tag3, user, function (err) {
            if (err) { next(err); }

            welcomeEmail(req.body.email, token, function () {
              res.render('thankyou');
            });

          });

        }).error(next);
      });
    }
  });
};

exports.cancelRegistration = function (req, res, next) {

  var removeTags = function (user, cb) {
    //user.getTags().then(function (tags) {
    req.app.get('database').model.TagsUsers.findAll({where: {UserId: user.id}}).then(function (rels) {

      async.each(rels, function (rel, done) {
        var vvv = rel.value;
        req.app.get('database').model.Tag.find({where: {id: rel.TagId}}).then(function (tag) {
          tag.decrement('count', { by: vvv}).then(function () {
            done();
          });
        });
      }, function () {
        //req.app.get('database').model.Tag.findAll({where: {count: 0}}).then(function () {
        req.app.get('database').model.TagsUsers.destroy({where: {UserId: user.id}}).then(function () {
          cb();
        });
        //});
      });
    });
  };

  req.assert('token', 'Invalid Input.').notEmpty().isHexadecimal();

  if (req.validationErrors()) {
    next(new Error('Danke, aber wir konnten deine Eingabe nicht annehmen.'));
  }

  req.app.get('database').model.User.find({ where: { token: req.params.token } }).success(function (user) {
    if (!user) {
      next(new Error('Abmeldungs-Token ist ungültig oder abgelaufen.'));
    }

    removeTags(user, function () {
      user.destroy().success(function () {
        req.app.get('database').sequelize.query("DELETE FROM Tags where count = 0").then(function () {
          res.render('cancel');
        });
      });
    });
  });

};

exports.sendCancelMail = function (req, res, next) {

  req.assert('cancel_email', 'Invalid Input.').notEmpty().isEmail();

  if (req.validationErrors()) {
    next(new Error('Danke, aber wir konnten deine Eingabe nicht annehmen.'));
  }

  var cancel_email = req.body.cancel_email,
    user =  req.app.get('database').model.User.find({ where: {email: cancel_email}}),
    token = user.token;

  cancelEmail(cancel_email, token, function () {
    res.render('cancelemailsent');
  });

};
<|endoftext|>


<commit_before>const electron = require('electron')
const uuid = require('uuid')
const fs = require('fs-extra')
const path = require('path')
const settingStore = require('../stores/settingStore')
const mailboxStore = require('../stores/mailboxStore')

const COOKIE_PERSIST_PERIOD = 1000 * 30 // 30 secs

class MailboxesSessionManager {

  /* ****************************************************************************/
  // Lifecycle
  /* ****************************************************************************/

  /**
  * @param mailboxWindow: the mailbox window instance we're working for
  */
  constructor (mailboxWindow) {
    this.mailboxWindow = mailboxWindow
    this.downloadsInProgress = { }
    this.persistCookieThrottle = { }

    this.__managed__ = new Set()
  }

  /* ****************************************************************************/
  // Utils
  /* ****************************************************************************/

  /**
  * @param partition: the partition id
  * @return the mailbox model for the partition
  */
  getMailboxFromPartition (partition) {
    return mailboxStore.getMailbox(partition.replace('persist:', ''))
  }

  /* ****************************************************************************/
  // Setup
  /* ****************************************************************************/

  /**
  * Starts managing a session
  * @param parition the name of the partion to manage
  */
  startManagingSession (partition) {
    if (this.__managed__.has(partition)) { return }

    const ses = electron.session.fromPartition(partition)
    ses.setDownloadPath(electron.app.getPath('downloads'))
    ses.on('will-download', (evt, item) => this.handleDownload(evt, item))
    ses.setPermissionRequestHandler(this.handlePermissionRequest)
    ses.webRequest.onCompleted((evt) => this.handleRequestCompleted(evt, ses, partition))

    this.__managed__.add(partition)
  }

  /* ****************************************************************************/
  // Permissions
  /* ****************************************************************************/

  /**
  * Handles a request for a permission from the client
  * @param webContents: the webcontents the request came from
  * @param permission: the permission name
  * @param callback: execute with response
  */
  handlePermissionRequest (webContents, permission, callback) {
    if (permission === 'notifications') {
      callback(false)
    } else {
      callback(true)
    }
  }

  /* ****************************************************************************/
  // Downloads
  /* ****************************************************************************/

  handleDownload (evt, item) {
    // If the user has chosen - auto save the item
    if (!settingStore.os.alwaysAskDownloadLocation && settingStore.os.defaultDownloadLocation) {
      const folderLocation = settingStore.os.defaultDownloadLocation
      const fpath = path.parse(item.getFilename() || 'untitled')

      // Check the file exists
      fs.ensureDirSync(folderLocation)

      // Keep trying a different variation of the filename until we find one that isn't in use
      let iter = 0
      while (true) {
        const testName = fpath.name + (iter === 0 ? '' : ' (' + iter + ')') + fpath.ext
        const testPath = path.join(folderLocation, testName)

        let exists
        try {
          fs.statSync(testPath)
          exists = true
        } catch (ex) {
          exists = false
        }

        if (exists) {
          iter++
        } else {
          item.setSavePath(testPath)
          break
        }
      }
    }

    // Report the progress to the window to display it
    const totalBytes = item.getTotalBytes()
    const id = uuid.v4()
    item.on('updated', () => {
      this.updateDownloadProgress(id, item.getReceivedBytes(), totalBytes)
    })
    item.on('done', (e, state) => {
      this.downloadFinished(id)
      if (state === 'completed') {
        const savePath = item.getSavePath()
        const saveName = path.basename(savePath)
        this.mailboxWindow.downloadCompleted(savePath, saveName)
      }
    })
  }

  /* ****************************************************************************/
  // Download Progress
  /* ****************************************************************************/

  /**
  * Updates the progress bar in the dock
  */
  updateWindowProgressBar () {
    const all = Object.keys(this.downloadsInProgress).reduce((acc, id) => {
      acc.received += this.downloadsInProgress[id].received
      acc.total += this.downloadsInProgress[id].total
      return acc
    }, { received: 0, total: 0 })

    if (all.received === 0 && all.total === 0) {
      this.mailboxWindow.setProgressBar(-1)
    } else {
      this.mailboxWindow.setProgressBar(all.received / all.total)
    }
  }

  /**
  * Updates the progress on a download
  * @param id: the download id
  * @param received: the bytes received
  * @param total: the total bytes to download
  */
  updateDownloadProgress (id, received, total) {
    this.downloadsInProgress[id] = this.downloadsInProgress[id] || {}
    this.downloadsInProgress[id].received = received
    this.downloadsInProgress[id].total = total
    this.updateWindowProgressBar()
  }

  /**
  * Indicates that a download has finished
  * @param id: the download id
  */
  downloadFinished (id) {
    delete this.downloadsInProgress[id]
    this.updateWindowProgressBar()
  }

  /* ****************************************************************************/
  // Requests
  /* ****************************************************************************/

  /**
  * Handles a request completing
  * @param evt: the event that fired
  * @param session: the session this request was for
  * @param partition: the partition string for this session
  */
  handleRequestCompleted (evt, session, partition) {
    this.artificiallyPersistCookies(session, partition)
  }

  /* ****************************************************************************/
  // Cookies
  /* ****************************************************************************/

  /**
  * Forces the cookies to persist artifically. This helps users using saml signin
  * @param session: the session this request was for
  * @param partition: the partition string for this session
  */
  artificiallyPersistCookies (session, partition) {
    if (this.persistCookieThrottle[partition] !== undefined) { return }
    const mailbox = this.getMailboxFromPartition(partition)
    if (!mailbox.artificiallyPersistCookies) { return }

    this.persistCookieThrottle[partition] = setTimeout(() => {
      session.cookies.get({ session: true }, (error, cookies) => {
        if (error || !cookies.length) {
          delete this.persistCookieThrottle[partition]
          return
        }
        cookies.forEach((cookie) => {
          const url = (cookie.secure ? 'https://' : 'http://') + cookie.domain + cookie.path
          session.cookies.remove(url, cookie.name, (error) => {
            if (error) {
              delete this.persistCookieThrottle[partition]
              return
            }
            const expire = new Date()
            expire.setYear(expire.getFullYear() + 1)
            const persistentCookie = {
              url: url,
              name: cookie.name,
              value: cookie.value,
              domain: cookie.domain,
              path: cookie.path,
              secure: cookie.secure,
              httpOnly: cookie.httpOnly,
              expirationDate: expire.getTime()
            }
            session.cookies.set(persistentCookie, (_) => {
              delete this.persistCookieThrottle[partition]
            })
          })
        })
      })
    }, COOKIE_PERSIST_PERIOD)
  }
}

module.exports = MailboxesSessionManager
<commit_msg>Persist cookie fix when you delete an account<commit_after>const electron = require('electron')
const uuid = require('uuid')
const fs = require('fs-extra')
const path = require('path')
const settingStore = require('../stores/settingStore')
const mailboxStore = require('../stores/mailboxStore')

const COOKIE_PERSIST_PERIOD = 1000 * 30 // 30 secs

class MailboxesSessionManager {

  /* ****************************************************************************/
  // Lifecycle
  /* ****************************************************************************/

  /**
  * @param mailboxWindow: the mailbox window instance we're working for
  */
  constructor (mailboxWindow) {
    this.mailboxWindow = mailboxWindow
    this.downloadsInProgress = { }
    this.persistCookieThrottle = { }

    this.__managed__ = new Set()
  }

  /* ****************************************************************************/
  // Utils
  /* ****************************************************************************/

  /**
  * @param partition: the partition id
  * @return the mailbox model for the partition
  */
  getMailboxFromPartition (partition) {
    return mailboxStore.getMailbox(partition.replace('persist:', ''))
  }

  /* ****************************************************************************/
  // Setup
  /* ****************************************************************************/

  /**
  * Starts managing a session
  * @param parition the name of the partion to manage
  */
  startManagingSession (partition) {
    if (this.__managed__.has(partition)) { return }

    const ses = electron.session.fromPartition(partition)
    ses.setDownloadPath(electron.app.getPath('downloads'))
    ses.on('will-download', (evt, item) => this.handleDownload(evt, item))
    ses.setPermissionRequestHandler(this.handlePermissionRequest)
    ses.webRequest.onCompleted((evt) => this.handleRequestCompleted(evt, ses, partition))

    this.__managed__.add(partition)
  }

  /* ****************************************************************************/
  // Permissions
  /* ****************************************************************************/

  /**
  * Handles a request for a permission from the client
  * @param webContents: the webcontents the request came from
  * @param permission: the permission name
  * @param callback: execute with response
  */
  handlePermissionRequest (webContents, permission, callback) {
    if (permission === 'notifications') {
      callback(false)
    } else {
      callback(true)
    }
  }

  /* ****************************************************************************/
  // Downloads
  /* ****************************************************************************/

  handleDownload (evt, item) {
    // If the user has chosen - auto save the item
    if (!settingStore.os.alwaysAskDownloadLocation && settingStore.os.defaultDownloadLocation) {
      const folderLocation = settingStore.os.defaultDownloadLocation
      const fpath = path.parse(item.getFilename() || 'untitled')

      // Check the file exists
      fs.ensureDirSync(folderLocation)

      // Keep trying a different variation of the filename until we find one that isn't in use
      let iter = 0
      while (true) {
        const testName = fpath.name + (iter === 0 ? '' : ' (' + iter + ')') + fpath.ext
        const testPath = path.join(folderLocation, testName)

        let exists
        try {
          fs.statSync(testPath)
          exists = true
        } catch (ex) {
          exists = false
        }

        if (exists) {
          iter++
        } else {
          item.setSavePath(testPath)
          break
        }
      }
    }

    // Report the progress to the window to display it
    const totalBytes = item.getTotalBytes()
    const id = uuid.v4()
    item.on('updated', () => {
      this.updateDownloadProgress(id, item.getReceivedBytes(), totalBytes)
    })
    item.on('done', (e, state) => {
      this.downloadFinished(id)
      if (state === 'completed') {
        const savePath = item.getSavePath()
        const saveName = path.basename(savePath)
        this.mailboxWindow.downloadCompleted(savePath, saveName)
      }
    })
  }

  /* ****************************************************************************/
  // Download Progress
  /* ****************************************************************************/

  /**
  * Updates the progress bar in the dock
  */
  updateWindowProgressBar () {
    const all = Object.keys(this.downloadsInProgress).reduce((acc, id) => {
      acc.received += this.downloadsInProgress[id].received
      acc.total += this.downloadsInProgress[id].total
      return acc
    }, { received: 0, total: 0 })

    if (all.received === 0 && all.total === 0) {
      this.mailboxWindow.setProgressBar(-1)
    } else {
      this.mailboxWindow.setProgressBar(all.received / all.total)
    }
  }

  /**
  * Updates the progress on a download
  * @param id: the download id
  * @param received: the bytes received
  * @param total: the total bytes to download
  */
  updateDownloadProgress (id, received, total) {
    this.downloadsInProgress[id] = this.downloadsInProgress[id] || {}
    this.downloadsInProgress[id].received = received
    this.downloadsInProgress[id].total = total
    this.updateWindowProgressBar()
  }

  /**
  * Indicates that a download has finished
  * @param id: the download id
  */
  downloadFinished (id) {
    delete this.downloadsInProgress[id]
    this.updateWindowProgressBar()
  }

  /* ****************************************************************************/
  // Requests
  /* ****************************************************************************/

  /**
  * Handles a request completing
  * @param evt: the event that fired
  * @param session: the session this request was for
  * @param partition: the partition string for this session
  */
  handleRequestCompleted (evt, session, partition) {
    this.artificiallyPersistCookies(session, partition)
  }

  /* ****************************************************************************/
  // Cookies
  /* ****************************************************************************/

  /**
  * Forces the cookies to persist artifically. This helps users using saml signin
  * @param session: the session this request was for
  * @param partition: the partition string for this session
  */
  artificiallyPersistCookies (session, partition) {
    if (this.persistCookieThrottle[partition] !== undefined) { return }
    const mailbox = this.getMailboxFromPartition(partition)
    if (!mailbox || !mailbox.artificiallyPersistCookies) { return }

    this.persistCookieThrottle[partition] = setTimeout(() => {
      session.cookies.get({ session: true }, (error, cookies) => {
        if (error || !cookies.length) {
          delete this.persistCookieThrottle[partition]
          return
        }
        cookies.forEach((cookie) => {
          const url = (cookie.secure ? 'https://' : 'http://') + cookie.domain + cookie.path
          session.cookies.remove(url, cookie.name, (error) => {
            if (error) {
              delete this.persistCookieThrottle[partition]
              return
            }
            const expire = new Date()
            expire.setYear(expire.getFullYear() + 1)
            const persistentCookie = {
              url: url,
              name: cookie.name,
              value: cookie.value,
              domain: cookie.domain,
              path: cookie.path,
              secure: cookie.secure,
              httpOnly: cookie.httpOnly,
              expirationDate: expire.getTime()
            }
            session.cookies.set(persistentCookie, (_) => {
              delete this.persistCookieThrottle[partition]
            })
          })
        })
      })
    }, COOKIE_PERSIST_PERIOD)
  }
}

module.exports = MailboxesSessionManager
<|endoftext|>


<commit_before>/* eslint-disable */
import { observe } from "mobx";
import ProjectStore from "../../../stores/domain/ProjectStore";
import Test from "../../../models/Test";

describe("Project Store", () => {
  it("should have a name", () => {
    const store = new ProjectStore("myStore");
    expect(store.name).toBe("myStore");
  });
  it("should observe the name change", () => {
    const store = new ProjectStore("myStore");
    const disposer = observe(store, "name", (change) => {
      expect(change.newValue).toBe("changed");
    });
    store.name = "changed";
    disposer();
  });
  it("should observe adding addition test to the store", () => {
    const store = new ProjectStore();
    const disposer = observe(store, "tests", (change) => {
      expect(change.newValue.length).toBe(1);
    });
    store.tests.push(new Test());
    disposer();
  });
});
<commit_msg>add tests globally to the store pool<commit_after>/* eslint-disable */
import { observe } from "mobx";
import ProjectStore from "../../../stores/domain/ProjectStore";
import Test from "../../../models/Test";

describe("Project Store", () => {
  it("should have a name", () => {
    const store = new ProjectStore("myStore");
    expect(store.name).toBe("myStore");
  });
  it("should observe the name change", () => {
    const store = new ProjectStore("myStore");
    const disposer = observe(store, "name", (change) => {
      expect(change.newValue).toBe("changed");
    });
    store.name = "changed";
    disposer();
  });
  it("should observe adding addition test to the store", () => {
    const store = new ProjectStore();
    const disposer = observe(store, "tests", (change) => {
      expect(change.newValue.length).toBe(1);
    });
    store.tests.push(new Test());
    disposer();
  });
  it("should add a new Test", () => {
    const store = new Store();
    expect(store.tests.length).toBe(0);
    store.addTest(new Test());
    expect(store.tests.length).toBe(1);
  });
  it("should throw if no Test was given", () => {
    const store = new Store();
    expect(() => store.addTest()).toThrowError("Expected to receive Test instead received undefined");
  });
  it("should throw if a different type was given", () => {
    const store = new Store();
    expect(() => store.addTest(1)).toThrowError("Expected to receive Test instead received Number");
  });
});
<|endoftext|>


<commit_before>/**
 * @module we-js-logger/util/common/rollbar
 * @description Shared rollbar helpers
 */

/**
 * Maps bunyan log levels to Rollbar levels
 * https://github.com/trentm/node-bunyan#levels
 * https://rollbar.com/docs/notifier/rollbar.js/api/#rollbardebuginfowarnwarningerrorcritical
 * @type {Object}
 */
const bunyanToRollbarLevelMap = {
  fatal: 'critical',
  error: 'error',
  warn: 'warning',
  info: 'info',
  debug: 'debug',
  trace: 'debug'
};

/**
 * @param  {String} level - bunyan log level
 * @returns {String} Rollbar log level
 */
export const bunyanLevelToRollbarLevelName = (level = bunyan.ERROR) => {
  const bunyanLevelName = bunyan.nameFromLevel[level];
  return bunyanToRollbarLevelMap[bunyanLevelName] || 'error';
};
<commit_msg>Add missing import statement<commit_after>import bunyan from 'bunyan';

/**
 * @module we-js-logger/util/common/rollbar
 * @description Shared rollbar helpers
 */

/**
 * Maps bunyan log levels to Rollbar levels
 * https://github.com/trentm/node-bunyan#levels
 * https://rollbar.com/docs/notifier/rollbar.js/api/#rollbardebuginfowarnwarningerrorcritical
 * @type {Object}
 */
const bunyanToRollbarLevelMap = {
  fatal: 'critical',
  error: 'error',
  warn: 'warning',
  info: 'info',
  debug: 'debug',
  trace: 'debug'
};

/**
 * @param  {String} level - bunyan log level
 * @returns {String} Rollbar log level
 */
export const bunyanLevelToRollbarLevelName = (level = bunyan.ERROR) => {
  const bunyanLevelName = bunyan.nameFromLevel[level];
  return bunyanToRollbarLevelMap[bunyanLevelName] || 'error';
};
<|endoftext|>


<commit_before>$.tablesorter.addParser({
	id: 'genericLink',
	is: function(s) {
		var exp = /<a[^>]*">(.*)<\/a>/.test(s);
		if(exp) this.type = (/<a[^>]*">(\d+)<\/a>/.test(s)) ? 'numeric' : 'text';
		return exp;
	},
	format: function(s) {
		var val = s.toLowerCase().match(/<a[^>]*">(.*)<\/a>/)[1];
		// format your data for normalization
		return (this.type == 'text') ?  $.trim(val) : $.tablesorter.formatFloat(val);
	},
	type: 'text'
});

$.tablesorter.addParser({
	id: 'currency',
	is: function(s) {
		return /^[£$€?.]/.test(s);
	},
	format: function(s) {
		return $.tablesorter.formatFloat(s.replace(new RegExp(/[^0-9.]/g),''));
	},
	type: 'numeric'
});

$.tablesorter.addParser({
	id: 'integer',
	is: function(s) {
		return /^\d+$/.test(s);
	},
	format: function(s) {
		return $.tablesorter.formatFloat(s);
	},
	type: 'numeric'
});

$.tablesorter.addParser({
	id: 'floating',
	is: function(s) {
		return /^(\+|-)?[0-9]+\.[0-9]+((E|e)(\+|-)?[0-9]+)?$/.test(s);
	},
	format: function(s) {
		return $.tablesorter.formatFloat(s.replace(new RegExp(/,|\./),''));
	},
	type: 'numeric'
});

$.tablesorter.addParser({
	id: 'ipAddress',
	is: function(s) {
		return /^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);
	},
	format: function(s) {
		var a = s.split('.');
		var r = '';
		for (var i = 0, item; item = a[i]; i++) {
		   if(item.length == 2) {
				r += '0' + item;
		   } else {
				r += item;
		   }
		}
		return $.tablesorter.formatFloat(s);
	},
	type: 'numeric'
});

$.tablesorter.addParser({
	id: 'url',
	is: function(s) {
		return /(https?|ftp|file):\/\//.test(s);
	},
	format: function(s) {
		return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//),''));
	},
	type: 'text'
});

$.tablesorter.addParser({
	id: 'isoDate',
	is: function(s) {
		return /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);
	},
	format: function(s) {
		return parseFloat((s != "") ? new Date(s.replace(new RegExp(/-/g),'/')).getTime() : "0");
	},
	type: 'numeric'
});

$.tablesorter.addParser({
	id: 'usLongDate',
	is: function(s) {
		return /^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/.test(s);
	},
	format: function(s) {
		return $.tablesorter.formatFloat(new Date(s).getTime());
	},
	type: 'numeric'
});
/*
$.tablesorter.addParser({
	id: 'shortDate',
	is: function(s) {
		return /\d{1,2}[\/-]\d{1,2}[\/-]\d{2,4}/.test(s);
	},
	format: function(s,defaults) {
		s = s.replace(new RegExp(/-/g),'/');
		if(defaults.dateFormat == "mm/dd/yyyy" || defaults.dateFormat == "mm-dd-yyyy") {
			// reformat the string in ISO format
			s = s.replace(new RegExp(/(\d{1,2})[\/-](\d{1,2})[\/-](\d{4})/), '$3/$1/$2');
		} else if(defaults.dateFormat == "dd/mm/yyyy" || defaults.dateFormat == "dd-mm-yyyy") {
			// reformat the string in ISO format
			s = s.replace(new RegExp(/(\d{1,2})[\/-](\d{1,2})[\/-](\d{4})/), '$3/$2/$1');
		} else if(defaults.dateFormat == "dd/mm/yy" || defaults.dateFormat == "dd-mm-yy") {
			s = s.replace(new RegExp(/(\d{1,2})[\/-](\d{1,2})[\/-](\d{2})/), '$1/$2/$3');
		}
		return $.tableSorter.utils.formatFloat((new Date(s)).getTime());
	},
	type: 'numeric'
});
*/
$.tablesorter.addParser({
    id: 'time',
    is: function(s) {
        return /^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);
    },
    format: function(s) {
        return $.tableSorter.utils.formatFloat((new Date("2000/01/01 " + s)).getTime());
    },
  type: 'numeric'
});<commit_msg>added parser to parser file that ought to correctly order numbers in scientific notation - treat them as floating-point numbers, not order alphanumerically<commit_after>$.tablesorter.addParser({
	id: 'genericLink',
	is: function(s) {
		var exp = /<a[^>]*">(.*)<\/a>/.test(s);
		if(exp) this.type = (/<a[^>]*">(\d+)<\/a>/.test(s)) ? 'numeric' : 'text';
		return exp;
	},
	format: function(s) {
		var val = s.toLowerCase().match(/<a[^>]*">(.*)<\/a>/)[1];
		// format your data for normalization
		return (this.type == 'text') ?  $.trim(val) : $.tablesorter.formatFloat(val);
	},
	type: 'text'
});

$.tablesorter.addParser({
	id: 'currency',
	is: function(s) {
		return /^[£$€?.]/.test(s);
	},
	format: function(s) {
		return $.tablesorter.formatFloat(s.replace(new RegExp(/[^0-9.]/g),''));
	},
	type: 'numeric'
});

$.tablesorter.addParser({
	id: 'integer',
	is: function(s) {
		return /^\d+$/.test(s);
	},
	format: function(s) {
		return $.tablesorter.formatFloat(s);
	},
	type: 'numeric'
});

$.tablesorter.addParser({
	id: 'floating',
	is: function(s) {
		return /^(\+|-)?[0-9]+\.[0-9]+((E|e)(\+|-)?[0-9]+)?$/.test(s);
	},
	format: function(s) {
		return $.tablesorter.formatFloat(s.replace(new RegExp(/,|\./),''));
	},
	type: 'numeric'
});

$.tablesorter.addParser({
	id: 'scientific',
	is: function(s) {
		return /^\de[\-]\d+$/.test(s);
	},
	format: function(s) {
		return $.tablesorter.formatFloat(s);
	},
	type: 'numeric'
});

$.tablesorter.addParser({
	id: 'ipAddress',
	is: function(s) {
		return /^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);
	},
	format: function(s) {
		var a = s.split('.');
		var r = '';
		for (var i = 0, item; item = a[i]; i++) {
		   if(item.length == 2) {
				r += '0' + item;
		   } else {
				r += item;
		   }
		}
		return $.tablesorter.formatFloat(s);
	},
	type: 'numeric'
});

$.tablesorter.addParser({
	id: 'url',
	is: function(s) {
		return /(https?|ftp|file):\/\//.test(s);
	},
	format: function(s) {
		return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//),''));
	},
	type: 'text'
});

$.tablesorter.addParser({
	id: 'isoDate',
	is: function(s) {
		return /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);
	},
	format: function(s) {
		return parseFloat((s != "") ? new Date(s.replace(new RegExp(/-/g),'/')).getTime() : "0");
	},
	type: 'numeric'
});

$.tablesorter.addParser({
	id: 'usLongDate',
	is: function(s) {
		return /^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/.test(s);
	},
	format: function(s) {
		return $.tablesorter.formatFloat(new Date(s).getTime());
	},
	type: 'numeric'
});
/*
$.tablesorter.addParser({
	id: 'shortDate',
	is: function(s) {
		return /\d{1,2}[\/-]\d{1,2}[\/-]\d{2,4}/.test(s);
	},
	format: function(s,defaults) {
		s = s.replace(new RegExp(/-/g),'/');
		if(defaults.dateFormat == "mm/dd/yyyy" || defaults.dateFormat == "mm-dd-yyyy") {
			// reformat the string in ISO format
			s = s.replace(new RegExp(/(\d{1,2})[\/-](\d{1,2})[\/-](\d{4})/), '$3/$1/$2');
		} else if(defaults.dateFormat == "dd/mm/yyyy" || defaults.dateFormat == "dd-mm-yyyy") {
			// reformat the string in ISO format
			s = s.replace(new RegExp(/(\d{1,2})[\/-](\d{1,2})[\/-](\d{4})/), '$3/$2/$1');
		} else if(defaults.dateFormat == "dd/mm/yy" || defaults.dateFormat == "dd-mm-yy") {
			s = s.replace(new RegExp(/(\d{1,2})[\/-](\d{1,2})[\/-](\d{2})/), '$1/$2/$3');
		}
		return $.tableSorter.utils.formatFloat((new Date(s)).getTime());
	},
	type: 'numeric'
});
*/
$.tablesorter.addParser({
    id: 'time',
    is: function(s) {
        return /^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);
    },
    format: function(s) {
        return $.tableSorter.utils.formatFloat((new Date("2000/01/01 " + s)).getTime());
    },
  type: 'numeric'
});<|endoftext|>