<commit_before>/*
 Copyright 2011 WebDriver committers
 Copyright 2011 Google Inc.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

goog.provide('SyntheticMouse');

goog.require('Utils');
goog.require('bot.ErrorCode');
goog.require('bot.Mouse');
goog.require('bot.action');
goog.require('bot.dom');
goog.require('bot.events');
goog.require('fxdriver.moz');
goog.require('fxdriver.utils');
goog.require('fxdriver.Logger');

var CC = Components.classes;
var CI = Components.interfaces;

SyntheticMouse = function() {
  this.wrappedJSObject = this;

  this.QueryInterface = fxdriver.moz.queryInterface(this,
      [CI.nsISupports, CI.wdIMouse]);

  // Declare the state we'll be using
  this.buttonDown = null;
  this.lastElement = null;
}

SyntheticMouse.newResponse = function(status, message) {
  return {
    status: status,
    message: message,
    QueryInterface: function(iid) {
      if (iid.equals(Components.interfaces.wdIStatus) ||
          iid.equals(Components.interfaces.nsISupports))
        return this;
      throw Components.results.NS_NOINTERFACE;
    }
  };
};

SyntheticMouse.prototype.isElementShown = function(element) {
  if (!bot.dom.isShown(element, /*ignoreOpacity=*/true)) {
    return SyntheticMouse.newResponse(bot.ErrorCode.ELEMENT_NOT_VISIBLE,
        'Element is not currently visible and so may not be interacted with')
  }
};

SyntheticMouse.prototype.getElement_ = function(coords) {
  return coords.auxiliary ?
      fxdriver.moz.unwrap(coords.auxiliary) : this.lastElement;
};

// wdIMouse

SyntheticMouse.prototype.move = function(target, xOffset, yOffset) {
  // TODO(simon): find the current "body" element iff element == null
  var element = target ?
      fxdriver.moz.unwrap(target) : this.lastElement;

  var origXOffset = xOffset !== undefined ? xOffset : 0;
  var origYOffset = yOffset !== undefined ? yOffset : 0;
   if (goog.isFunction(element.scrollIntoView)) {
     element.scrollIntoView();
  }

  // The following code assumes xOffset, yOffset are absolute in the DOM.
  // If the page was scrolled, the scroll offset should be substracted
  // from the provided offset since the movement events are generated
  // relative to the viewport.
  var doc = goog.dom.getOwnerDocument(element);
  var currentScroll = goog.dom.getDomHelper(doc).getDocumentScroll();
  xOffset -= currentScroll.x;
  yOffset -= currentScroll.y;

  // If the mouse was already moved within this document, assume the last
  // coordinates of the mouse are the location of lastElement.
  // This only makes sense, of course, if the last element and the current
  // element belong to the same document.
  if (this.lastElement && element && this.lastElement != element &&
      (this.lastElement.ownerDocument == element.ownerDocument)) {
    var currLoc = Utils.getElementLocation(this.lastElement);
    var targetLoc = Utils.getElementLocation(element);
    xOffset += targetLoc['x'] - currLoc['x'];
    yOffset += targetLoc['y'] - currLoc['y'];
  }

  this.lastElement = element;
  var pos = Utils.getElementLocation(element);
  var owner = goog.dom.getOwnerDocument(element);
  bot.setWindow(goog.dom.getWindow(owner));

  // Which element shall we pretend to be leaving?
  var parent = bot.dom.getParentElement(element);

  // TODO(simon): if no offset is specified, use the centre of the element
  var fireAndCheck = function(e, eventName, opt_coordinates) {
    if (!e) {
      return false;
    }
    bot.events.fire(e, eventName, opt_coordinates);
    return true;
  };

  var button = this.buttonDown;
  var botCoords = {
    'clientX': pos.x,
    'clientY': pos.y,
    'button': button,
    'related': parent
  };

  var intermediateSteps = 3;
  var xInc = Math.floor(xOffset / intermediateSteps);
  var yInc = Math.floor(yOffset / intermediateSteps);
  var currX = pos.x;
  var currY = pos.y;

  var proceed = fireAndCheck(parent, goog.events.EventType.MOUSEOUT, {'related': element}) &&
      fireAndCheck(element, goog.events.EventType.MOUSEOVER, botCoords);
    for (var i = 0; i < intermediateSteps && proceed; i++) {
      botCoords['clientX'] = xInc + currX;  currX += xInc;
      botCoords['clientY'] = yInc + currY;  currY += yInc;
      proceed = fireAndCheck(element, goog.events.EventType.MOUSEMOVE, botCoords);
  }

  botCoords['clientX'] = (pos.x + xOffset);
  botCoords['clientY'] = (pos.y + yOffset);

  proceed = fireAndCheck(element, goog.events.EventType.MOUSEMOVE, botCoords);

  var newPos = Utils.getElementLocation(this.lastElement);
  var xDifference = Math.abs(botCoords['clientX'] - newPos.x);
  var yDifference = Math.abs(botCoords['clientY'] - newPos.y);
  // Allow a 3-pixel deviation, since synthetic events are not as accurate
  // as native ones.
  if (((origXOffset != 0) || (origYOffset != 0)) &&
      ((xDifference > 3) || (yDifference > 3))) {
    return SyntheticMouse.newResponse(bot.ErrorCode.MOVE_TARGET_OUT_OF_BOUNDS,
        'Move could not be performed as far as requested: (' + newPos.x + ' ' +
        newPos.y + ') targeted  (' + botCoords['clientX'] + ' ' + botCoords['clientY'] + ')');
  }

  if (!proceed || !bot.dom.isShown(element, /*ignoreOpacity=*/true)) {
    return SyntheticMouse.newResponse(bot.ErrorCode.SUCCESS, "ok");
  }

  bot.events.fire(element, goog.events.EventType.MOUSEOVER, botCoords);

  return SyntheticMouse.newResponse(bot.ErrorCode.SUCCESS, "ok");
};

SyntheticMouse.prototype.click = function(target) {

  // No need to unwrap the target. All information is provided by the wrapped
  // version, and unwrapping does not work for all firefox versions.
  var element = target ? target : this.lastElement;

  var error = this.isElementShown(element);
  if (error) {
    return error;
  }

  // Check to see if this is an option element. If it is, and the parent isn't a multiple
  // select, then click on the select first.
  var tagName = element.tagName.toLowerCase();
  if ("option" == tagName) {
    var parent = element;
    while (parent.parentNode != null && parent.tagName.toLowerCase() != "select") {
      parent = parent.parentNode;
    }

    if (parent && parent.tagName.toLowerCase() == "select" && !parent.multiple) {
      bot.action.click(parent);
    }
  }

  fxdriver.Logger.dumpn("About to do a bot.action.click on " + element);
  bot.action.click(element);

  return SyntheticMouse.newResponse(bot.ErrorCode.SUCCESS, "ok");
};

SyntheticMouse.prototype.contextClick = function(target) {

  // No need to unwrap the target. All information is provided by the wrapped
  // version, and unwrapping does not work for all firefox versions.
  var element = target ? target : this.lastElement;

  var error = this.isElementShown(element);
  if (error) {
    return error;
  }

  fxdriver.Logger.dumpn("About to do a bot.action.rightClick on " + element);
  bot.action.rightClick(element);

  return SyntheticMouse.newResponse(bot.ErrorCode.SUCCESS, "ok");
};

SyntheticMouse.prototype.doubleClick = function(target) {
  var element = target ? target : this.lastElement;

  var error = this.isElementShown(element);
  if (error) {
    return error;
  }

  fxdriver.Logger.dumpn("About to do a bot.action.doubleClick on " + element);
  bot.action.doubleClick(element);

  return SyntheticMouse.newResponse(bot.ErrorCode.SUCCESS, "ok");
};

SyntheticMouse.prototype.down = function(coordinates) {
  var element = this.getElement_(coordinates);

  var pos = goog.style.getClientPosition(element);

  // TODO(simon): This implementation isn't good enough. Again
  // Defaults to left mouse button, which is right.
  this.buttonDown = bot.Mouse.Button.LEFT;
  var botCoords = {
    'clientX': coordinates['x'] + pos.x,
    'clientY': coordinates['y'] + pos.y,
    'button': bot.Mouse.Button.LEFT
  };
  bot.events.fire(element, goog.events.EventType.MOUSEDOWN, botCoords);

  return SyntheticMouse.newResponse(bot.ErrorCode.SUCCESS, "ok");
};

SyntheticMouse.prototype.up = function(coordinates) {
  var element = this.getElement_(coordinates);

  var pos = goog.style.getClientPosition(element);

  // TODO(simon): This implementation isn't good enough. Again
  // Defaults to left mouse button, which is the correct one.
  var button = this.buttonDown;
  var botCoords = {
    'clientX': coordinates['x'] + pos.x,
    'clientY': coordinates['y'] + pos.y,
    'button': button
  };
  bot.events.fire(element, goog.events.EventType.MOUSEMOVE, botCoords);
  bot.events.fire(element, goog.events.EventType.MOUSEUP, botCoords);

  this.buttonDown = null;

  return SyntheticMouse.newResponse(bot.ErrorCode.SUCCESS, "ok");
};

// And finally, registering
SyntheticMouse.prototype.classDescription = "Pure JS implementation of a mouse";
SyntheticMouse.prototype.contractID = '@googlecode.com/webdriver/syntheticmouse;1';
SyntheticMouse.prototype.classID = Components.ID('{E8F9FEFE-C513-4097-98BE-BE00A41D3645}');

/** @const */ var components = [SyntheticMouse];
var NSGetFactory, NSGetModule;

fxdriver.moz.load('resource://gre/modules/XPCOMUtils.jsm');

if (XPCOMUtils.generateNSGetFactory) {
  NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
} else {
  NSGetModule = XPCOMUtils.generateNSGetModule(components);
}

goog.exportSymbol('SyntheticMouse', SyntheticMouse);
goog.exportSymbol('SyntheticMouse.prototype.down', SyntheticMouse.prototype.down);
goog.exportSymbol('SyntheticMouse.prototype.up', SyntheticMouse.prototype.up);

goog.exportSymbol('SyntheticMouse.prototype.move', SyntheticMouse.prototype.move);

goog.exportSymbol('SyntheticMouse.prototype.click', SyntheticMouse.prototype.click);
goog.exportSymbol('SyntheticMouse.prototype.doubleClick', SyntheticMouse.prototype.doubleClick);
goog.exportSymbol('SyntheticMouse.prototype.contextClick', SyntheticMouse.prototype.contextClick);
<commit_msg>SimonStewart: Modifying the synthesizedMouse to detect whether we're about to move 'something' out of the window of the current document. I suspect that this might cause some problems, but the logic looks good to me<commit_after>/*
 Copyright 2011 WebDriver committers
 Copyright 2011 Google Inc.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

goog.provide('SyntheticMouse');

goog.require('Utils');
goog.require('bot.ErrorCode');
goog.require('bot.Mouse');
goog.require('bot.action');
goog.require('bot.dom');
goog.require('bot.events');
goog.require('fxdriver.moz');
goog.require('fxdriver.utils');
goog.require('fxdriver.Logger');

var CC = Components.classes;
var CI = Components.interfaces;

SyntheticMouse = function() {
  this.wrappedJSObject = this;

  this.QueryInterface = fxdriver.moz.queryInterface(this,
      [CI.nsISupports, CI.wdIMouse]);

  // Declare the state we'll be using
  this.buttonDown = null;
  this.lastElement = null;
}

SyntheticMouse.newResponse = function(status, message) {
  return {
    status: status,
    message: message,
    QueryInterface: function(iid) {
      if (iid.equals(Components.interfaces.wdIStatus) ||
          iid.equals(Components.interfaces.nsISupports))
        return this;
      throw Components.results.NS_NOINTERFACE;
    }
  };
};

SyntheticMouse.prototype.isElementShown = function(element) {
  if (!bot.dom.isShown(element, /*ignoreOpacity=*/true)) {
    return SyntheticMouse.newResponse(bot.ErrorCode.ELEMENT_NOT_VISIBLE,
        'Element is not currently visible and so may not be interacted with')
  }
};

SyntheticMouse.prototype.getElement_ = function(coords) {
  return coords.auxiliary ?
      fxdriver.moz.unwrap(coords.auxiliary) : this.lastElement;
};

// wdIMouse

SyntheticMouse.prototype.move = function(target, xOffset, yOffset) {
  // TODO(simon): find the current "body" element iff element == null
  var element = target ?
      fxdriver.moz.unwrap(target) : this.lastElement;

   if (goog.isFunction(element.scrollIntoView)) {
     element.scrollIntoView();
  }

  // The following code assumes xOffset, yOffset are absolute in the DOM.
  // If the page was scrolled, the scroll offset should be subtracted
  // from the provided offset since the movement events are generated
  // relative to the viewport.
  var doc = goog.dom.getOwnerDocument(element);
  var currentScroll = goog.dom.getDomHelper(doc).getDocumentScroll();
  xOffset -= currentScroll.x;
  yOffset -= currentScroll.y;

  // If the mouse was already moved within this document, assume the last
  // coordinates of the mouse are the location of lastElement.
  // This only makes sense, of course, if the last element and the current
  // element belong to the same document.
  if (this.lastElement && element && this.lastElement != element &&
      (this.lastElement.ownerDocument == element.ownerDocument)) {
    var currLoc = Utils.getElementLocation(this.lastElement);
    var targetLoc = Utils.getElementLocation(element);
    xOffset += targetLoc['x'] - currLoc['x'];
    yOffset += targetLoc['y'] - currLoc['y'];
  }

  this.lastElement = element;
  var pos = Utils.getElementLocation(element);
  var owner = goog.dom.getOwnerDocument(element);
  var win = goog.dom.getWindow(owner);
  bot.setWindow(win);

  // Are we about to be dragged out of the window?
  var helper = new goog.dom.DomHelper(owner);

  var viewportSize = goog.dom.getViewportSize(win);
  var docHeight = helper.getDocumentHeight();
  var docWidth = owner.body.offsetWidth;

  var maxHeight = docHeight < viewportSize.height ? viewportSize.height : docHeight;
  var maxWidth = docWidth < viewportSize.width ? viewportSize.width : docWidth;

  var targetX = pos.x + xOffset;
  var targetY = pos.y + yOffset;

  if (targetX > maxWidth || targetY > maxHeight) {
    return SyntheticMouse.newResponse(bot.ErrorCode.MOVE_TARGET_OUT_OF_BOUNDS,
        'Requested location (' + targetX + ', ' + targetY +
        ') is outside the bounds of the document (' + maxWidth + ', ' + maxHeight + ')');
  }

  // Which element shall we pretend to be leaving?
  var parent = bot.dom.getParentElement(element);

  // TODO(simon): if no offset is specified, use the centre of the element
  var fireAndCheck = function(e, eventName, opt_coordinates) {
    if (!e) {
      return false;
    }
    bot.events.fire(e, eventName, opt_coordinates);
    return true;
  };

  var button = this.buttonDown;
  var botCoords = {
    'clientX': pos.x,
    'clientY': pos.y,
    'button': button,
    'related': parent
  };

  var intermediateSteps = 3;
  var xInc = Math.floor(xOffset / intermediateSteps);
  var yInc = Math.floor(yOffset / intermediateSteps);
  var currX = pos.x;
  var currY = pos.y;

  var proceed = fireAndCheck(parent, goog.events.EventType.MOUSEOUT, {'related': element}) &&
      fireAndCheck(element, goog.events.EventType.MOUSEOVER, botCoords);
    for (var i = 0; i < intermediateSteps && proceed; i++) {
      botCoords['clientX'] = xInc + currX;  currX += xInc;
      botCoords['clientY'] = yInc + currY;  currY += yInc;
      proceed = fireAndCheck(element, goog.events.EventType.MOUSEMOVE, botCoords);
  }

  botCoords['clientX'] = (pos.x + xOffset);
  botCoords['clientY'] = (pos.y + yOffset);

  proceed = fireAndCheck(element, goog.events.EventType.MOUSEMOVE, botCoords);

  var newPos = Utils.getElementLocation(this.lastElement);
  var xDifference = Math.abs(botCoords['clientX'] - newPos.x);
  var yDifference = Math.abs(botCoords['clientY'] - newPos.y);
  // Allow a 3-pixel deviation, since synthetic events are not as accurate
  // as native ones.
//  if (((origXOffset != 0) || (origYOffset != 0)) &&
//      ((xDifference > 3) || (yDifference > 3))) {
//    return SyntheticMouse.newResponse(bot.ErrorCode.MOVE_TARGET_OUT_OF_BOUNDS,
//        'Move could not be performed as far as requested: (' + newPos.x + ' ' +
//        newPos.y + ') targeted  (' + botCoords['clientX'] + ' ' + botCoords['clientY'] + ')');
//  }

  if (!proceed || !bot.dom.isShown(element, /*ignoreOpacity=*/true)) {
    return SyntheticMouse.newResponse(bot.ErrorCode.SUCCESS, "ok");
  }

  bot.events.fire(element, goog.events.EventType.MOUSEOVER, botCoords);

  return SyntheticMouse.newResponse(bot.ErrorCode.SUCCESS, "ok");
};

SyntheticMouse.prototype.click = function(target) {

  // No need to unwrap the target. All information is provided by the wrapped
  // version, and unwrapping does not work for all firefox versions.
  var element = target ? target : this.lastElement;

  var error = this.isElementShown(element);
  if (error) {
    return error;
  }

  // Check to see if this is an option element. If it is, and the parent isn't a multiple
  // select, then click on the select first.
  var tagName = element.tagName.toLowerCase();
  if ("option" == tagName) {
    var parent = element;
    while (parent.parentNode != null && parent.tagName.toLowerCase() != "select") {
      parent = parent.parentNode;
    }

    if (parent && parent.tagName.toLowerCase() == "select" && !parent.multiple) {
      bot.action.click(parent);
    }
  }

  fxdriver.Logger.dumpn("About to do a bot.action.click on " + element);
  bot.action.click(element);

  return SyntheticMouse.newResponse(bot.ErrorCode.SUCCESS, "ok");
};

SyntheticMouse.prototype.contextClick = function(target) {

  // No need to unwrap the target. All information is provided by the wrapped
  // version, and unwrapping does not work for all firefox versions.
  var element = target ? target : this.lastElement;

  var error = this.isElementShown(element);
  if (error) {
    return error;
  }

  fxdriver.Logger.dumpn("About to do a bot.action.rightClick on " + element);
  bot.action.rightClick(element);

  return SyntheticMouse.newResponse(bot.ErrorCode.SUCCESS, "ok");
};

SyntheticMouse.prototype.doubleClick = function(target) {
  var element = target ? target : this.lastElement;

  var error = this.isElementShown(element);
  if (error) {
    return error;
  }

  fxdriver.Logger.dumpn("About to do a bot.action.doubleClick on " + element);
  bot.action.doubleClick(element);

  return SyntheticMouse.newResponse(bot.ErrorCode.SUCCESS, "ok");
};

SyntheticMouse.prototype.down = function(coordinates) {
  var element = this.getElement_(coordinates);

  var pos = goog.style.getClientPosition(element);

  // TODO(simon): This implementation isn't good enough. Again
  // Defaults to left mouse button, which is right.
  this.buttonDown = bot.Mouse.Button.LEFT;
  var botCoords = {
    'clientX': coordinates['x'] + pos.x,
    'clientY': coordinates['y'] + pos.y,
    'button': bot.Mouse.Button.LEFT
  };
  bot.events.fire(element, goog.events.EventType.MOUSEDOWN, botCoords);

  return SyntheticMouse.newResponse(bot.ErrorCode.SUCCESS, "ok");
};

SyntheticMouse.prototype.up = function(coordinates) {
  var element = this.getElement_(coordinates);

  var pos = goog.style.getClientPosition(element);

  // TODO(simon): This implementation isn't good enough. Again
  // Defaults to left mouse button, which is the correct one.
  var button = this.buttonDown;
  var botCoords = {
    'clientX': coordinates['x'] + pos.x,
    'clientY': coordinates['y'] + pos.y,
    'button': button
  };
  bot.events.fire(element, goog.events.EventType.MOUSEMOVE, botCoords);
  bot.events.fire(element, goog.events.EventType.MOUSEUP, botCoords);

  this.buttonDown = null;

  return SyntheticMouse.newResponse(bot.ErrorCode.SUCCESS, "ok");
};

// And finally, registering
SyntheticMouse.prototype.classDescription = "Pure JS implementation of a mouse";
SyntheticMouse.prototype.contractID = '@googlecode.com/webdriver/syntheticmouse;1';
SyntheticMouse.prototype.classID = Components.ID('{E8F9FEFE-C513-4097-98BE-BE00A41D3645}');

/** @const */ var components = [SyntheticMouse];
var NSGetFactory, NSGetModule;

fxdriver.moz.load('resource://gre/modules/XPCOMUtils.jsm');

if (XPCOMUtils.generateNSGetFactory) {
  NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
} else {
  NSGetModule = XPCOMUtils.generateNSGetModule(components);
}

goog.exportSymbol('SyntheticMouse', SyntheticMouse);
goog.exportSymbol('SyntheticMouse.prototype.down', SyntheticMouse.prototype.down);
goog.exportSymbol('SyntheticMouse.prototype.up', SyntheticMouse.prototype.up);

goog.exportSymbol('SyntheticMouse.prototype.move', SyntheticMouse.prototype.move);

goog.exportSymbol('SyntheticMouse.prototype.click', SyntheticMouse.prototype.click);
goog.exportSymbol('SyntheticMouse.prototype.doubleClick', SyntheticMouse.prototype.doubleClick);
goog.exportSymbol('SyntheticMouse.prototype.contextClick', SyntheticMouse.prototype.contextClick);
<|endoftext|>


<commit_before>function parseCashdeskLog(progress, productList, taxList, input) {
	//split input into purchases
	var purchases = [];
	progress.setData(purchases);
	var purchase = null;
	var lines = input.split('\n');
	for (var i = 0; i < lines.length; i++) {
		var match = lines[i].match(/([^"]*;)+"\s*(\d+)-\s*(\d+)-\s*(\d+)\s*\d+\s*(\d+):(\d+)\s*"/);
		if (match) {
			if (purchase != null) {
				purchases.push(purchase);
			}
			purchase = {
				'day': match[2],
				'month': match[3],
				'year': match[4],
				'hour': match[5],
				'minute': match[6],
				'isBulletin': false,
				'lines': []
			};
		} else {
			if (purchase != null) {
				purchase.lines.push(lines[i]);
			}
		}
	}
	progress.getCurrentStage().getCurrentSubstage().completeSubstage();

	// parse purchase lines
	var bulletin = null;
	purchases = purchases.map(function(p) {
		var summaryStartIndex = -1;
		for (var i = 1; i < p.lines.length; i++) {
			if (p.lines[i].indexOf('Gesamt') > -1) {
				summaryStartIndex = i;
				break;
			} else if (p.lines[i].indexOf('Tagesbericht') > -1) {
				p.isBulletin = true;
				break;
			}
		}
		if (summaryStartIndex == -1 && !p.isBulletin) {
			return false;
		}
		p.id = p.lines[0].match(/([^"]*;)+"\s*([^"]+)\s*"/)[2].trim();
		if (p.isBulletin) {
			bulletin = p;
			p.summary = {
				'counter': p.lines[4].match(/([^"]*;)+"\s*Z[^\d]*([^"]*)"/),
				'quantity': p.lines[5].match(/([^"]*;)+"\s*A[^\d]*([^"]*)"/),
				'price': p.lines[6].match(/([^"]*;)+"\s*B[^\d]*([^"]*)"/)
			};
			if (p.summary.counter && p.summary.quantity && p.summary.price) {
				p.summary.counter = p.summary.counter[2];
				p.summary.quantity = p.summary.quantity[2];
				p.summary.price = p.summary.price[2];
			} else {
				throw 'Bulletin not parsable: ' + JSON.stringify(p, null, 2);
			}

			// parse products
			p.products = [];
			var taxesStartIndex = -1;
			for (var i = 8; i < p.lines.length; i++) {
				if (p.lines[i].indexOf('Steuern') > -1) {
					taxesStartIndex = i;
					break;
				}
			}
			for (var i = 8; i < taxesStartIndex; i+=4) {
				var product = {
					'id': p.lines[i].match(/([^"]*;)+"\s*(#\d+)/)[2],
					'name': p.lines[i+1].match(/([^"]*;)+"\s*([^"]*?)\s*"/)[2],
					'quantity': p.lines[i+2].match(/([^"]*;)+"\s*A[^ ]+ +([^"]*)"/)[2],
					'price': p.lines[i+3].match(/([^"]*;)+"\s*B[^ ]+ +([^"]*)"/)[2]
				};
				product.number = parseInt(product.id.substr(1));
				p.products.push(product);
			}
			var paymentMethodsStartIndex = -1;
			for (var i = taxesStartIndex; i < p.lines.length; i++) {
				if (p.lines[i].indexOf('Zahlungsarten') > -1) {
					paymentMethodsStartIndex = i;
					break;
				}
			}
			p.summary.taxes = {};
			for (var i = taxesStartIndex + 1; i < paymentMethodsStartIndex; i+=4) {
				p.summary.taxes[p.lines[i].match(/([^"]*;)+"\s*MwSt\s*([^"]*?)\s*"/)[2]] = p.lines[i+3].match(/([^"]*;)+"\s*MwSt\s*([^"]*)"/)[2];
			}
		} else {
			p.products = [];
			// parse products
			for (var i = 1; i < summaryStartIndex; i++) {
				var product = parseProductLine(p.lines[i]);
				if (product) {
					if (product.quantity[0] === '-') {
						if (p.lines[i-1].indexOf('Retour') > -1) {
							product.note = 'Retour';
						} else if (p.lines[i-1].indexOf('Sofort Storno') > -1) {
							if (p.products.length > 0 && ('-' + p.products[p.products.length - 1].quantity) === product.quantity) {
								p.products.splice(p.products.length - 1, 1);
								continue;
							} else {
								product.note = 'Sofort Storno';
							}
						}
					}
					p.products.push(product);
				}
			}

			// parse summary
			p.summary = parseProductLine(p.lines[summaryStartIndex]);
			delete p.summary.name;
			delete p.summary.note;
			p.summary.taxes = {};
			for (var i = summaryStartIndex + 1; i < p.lines.length; i++) {
				var match = p.lines[i].match(/([^"]*;)+"\s+MwSt\s*(\d+%)\s*(-?\d+\.\d+)/);
				if (match) {
					p.summary.taxes[match[2]] = match[3];
				}
			}
		}
		return p;
	});
	progress.getCurrentStage().getCurrentSubstage().completeSubstage();

	// remove invalid purchases
	purchases = purchases.filter(function(p) {
		return p != false && p.products.length > 0;
	});

	// parse numbers
	purchases.forEach(function(purchase) {
		purchase.products.forEach(function(product) {
			product.price = parseFloat(product.price.replace(/,/g, ''));
			product.quantity = parseFloat(product.quantity.replace(/,/g, ''));
		});
		purchase.summary.quantity = parseInt(purchase.summary.quantity);
		purchase.summary.price = parseFloat(purchase.summary.price.replace(/,/g, ''));
		for (var tax in purchase.summary.taxes) {
			purchase.summary.taxes[tax] = parseFloat(purchase.summary.taxes[tax].replace(/,/g, ''));
		}
	});
	progress.getCurrentStage().getCurrentSubstage().completeSubstage();

	// remove bulletin
	purchases = purchases.filter(function(p) {
		return !p.isBulletin;
	});

	// update product names of purchases
	var unmatchedProductNames = [];
	purchases.forEach(function(purchase) {
		purchase.products.forEach(function(product) {
			var possibleProducts = [];
			bulletin.products.forEach(function(p) {
				if (p.name.indexOf(product.name) > -1) {
					possibleProducts.push(p);
				}
			});
			if (possibleProducts.length === 0) {
				//throw 'No product name matched "' + product.name + '"!';
				var foundName = false;
				for (var i = 0; i < unmatchedProductNames.length; i++) {
					if (unmatchedProductNames[i].indexOf(product.name) > -1 || product.name.indexOf(unmatchedProductNames[i])) {
						product.name = unmatchedProductNames[i];
						foundName = true;
						break;
					}
				}
				if (!foundName) {
					unmatchedProductNames.push(product.name);
				}
				product.note = "Product name not found in bulletin";
			} else if (possibleProducts.length === 1) {
				product.name = productList[possibleProducts[0].number].name;
			} else {
				var pricePerQuantity = product.price / product.quantity;
				var minDelta = Number.POSITIVE_INFINITY;
				var prod = null;
				possibleProducts.forEach(function(p) {
					var delta = Math.abs(pricePerQuantity - p.price / p.quantity);
					if (delta < minDelta) {
						minDelta = delta;
						prod = p;
					}
				});
				product.name = productList[prod.number].name;
			}
		});
	});
	progress.getCurrentStage().getCurrentSubstage().completeSubstage();

	// combine equal products of purchases
	var productCombinations = 0;
	purchases.forEach(function(purchase) {
		for (var i = 0; i < purchase.products.length; i++) {
			for (var j = i + 1; j < purchase.products.length; j++) {
				if (purchase.products[i].name === purchase.products[j].name) {
					productCombinations++;
					var p = purchase.products.splice(j, 1)[0];
					purchase.products[i].price += p.price;
					purchase.products[i].quantity += p.quantity;
					purchase.products[i].note = 'Kombiniert';
					j--;
				}
			}
		}
	});
	progress.getCurrentStage().getCurrentSubstage().completeSubstage();

	// update bulletin product names
	bulletin.products.forEach(function(product) {
		product.name = productList[product.number].name;
	});

	return {
		'bulletin': bulletin,
		'purchases': purchases
	};
}

function parseProductLine(line) {
	var match = line.match(/([^"]*;)+"(-?\d*(\.\d+)?)\s+(.*?)\s+(-?\d+\.\d+)/);
	if (match) {
		return {
			'quantity': match[2],
			'name': match[4],
			'price': match[5],
			'note': ''
		};
	} else {
		return false;
	}
}

function createCsvHeader(productList, taxList) {
	var csv = 'RechnungsNr;Tag;Monat;Jahr;Stunde;Minute;Gesamtpreis';
	taxList.forEach(function(tax) {
		csv += ';' + tax + ' MwSt Anteil';
	});
	for (var i in productList) {
		csv += ';' + productList[i].name + ' Menge;' + productList[i].name + ' Preis';
	};
	return csv;
}

function createCsv(productList, taxList, decimalMark, items) {
	var csv = '';
	items.forEach(function(item) {
		csv += '\n';
		csv += [item.id, item.day, item.month, item.year, item.hour, item.minute, item.summary.price].join(';');
		taxList.forEach(function(tax) {
			csv += ';';
			if (item.summary.taxes[tax] != undefined) {
				csv += item.summary.taxes[tax];
			} else {
				csv += '0';
			}
		});
		for (var pIndex in productList) {
			csv += ';';
			var hasProduct = false;
			for (var i = 0; i < item.products.length; i++) {
				if (productList[pIndex].name === item.products[i].name) {
					csv += [item.products[i].quantity, item.products[i].price].join(';');
					hasProduct = true;
					break;
				}
			}
			if (!hasProduct) {
				csv += '0;0';
			}
		}
	});
	return createCsvHeader(productList, taxList) + csv.replace(/\./g, decimalMark);
}

function readProductList(input) {
	var lines = input.split('\n');
	var productList = [];
	for (var i = 1; i < lines.length; i++) {
		var spl = lines[i].split(';');
		var product = {
			'number': parseFloat(spl[0].trim()),
			'name': spl[1].trim()
		};
		productList[product.number] = product;
	}
	return productList;
}

onmessage = function(e) {
	var base = e.data.splice(0, 1)[0];
	eval(base);
	var input = e.data.splice(0, 1)[0];
	var args = e.data;

	var rawStages = [
		'Started',
		'Read \'Products file\' and \'Taxes file\'',
		{
			'name': 'Parse input files',
			'stages': input.filter(function(i) {
				return i.name != args[1] && i.name != args[2];
			}).map(function(i) {
				return {
					'name': 'Parse ' + i.name,
					'stages': ['Split file into purchases', 'Parse purchases', 'Parse numbers', 'Update product names', 'Combine equal products']
				}
			})
		},
		'Create csv files'
	];
	var progress = new Progress(createStages(rawStages));
	progress.getCurrentStage().complete();

	var productList = null;
	var taxList = null;
	for (var i = 0; i < input.length && (productList == null || taxList == null); i++) {
		if (input[i].name == args[1]) { // i is product-list
			productList = input.splice(i, 1)[0];
			productList = readProductList(productList.content);
			i--;
		} else if (input[i].name == args[2]) { // i is tax-list
			taxList = input.splice(i, 1)[0];
			taxList = taxList.content.split(';');
			i--;
		}
	}
	if (productList == null) {
		throw 'Product list not found in input files!';
	} else if (taxList == null) {
		throw 'Tax list not found in input files!';
	}
	progress.getCurrentStage().complete();

	var purchases = [];
	var bulletins = [];
	input.forEach(function(i) {
		var x = parseCashdeskLog(progress, productList, taxList, i.content);
		purchases = purchases.concat(x.purchases);
		bulletins.push(x.bulletin);
		progress.getCurrentStage().completeSubstage();
	});
	progress.setData({'purchases': purchases, 'bulletins': bulletins});
	progress.getCurrentStage().complete();

	var result = [{
		'name': 'Einkäufe.csv',
		'content': createCsv(productList, taxList, args[0], purchases)
	}, {
		'name': 'Tagesberichte.csv',
		'content': createCsv(productList, taxList, args[0], bulletins)
	}];
	progress.setData(result);
	progress.getCurrentStage().complete();
}
<commit_msg>Move onmessage to top<commit_after>onmessage = function(e) {
	var base = e.data.splice(0, 1)[0];
	eval(base);
	var input = e.data.splice(0, 1)[0];
	var args = e.data;

	var rawStages = [
		'Started',
		'Read \'Products file\' and \'Taxes file\'',
		{
			'name': 'Parse input files',
			'stages': input.filter(function(i) {
				return i.name != args[1] && i.name != args[2];
			}).map(function(i) {
				return {
					'name': 'Parse ' + i.name,
					'stages': ['Split file into purchases', 'Parse purchases', 'Parse numbers', 'Update product names', 'Combine equal products']
				}
			})
		},
		'Create csv files'
	];
	var progress = new Progress(createStages(rawStages));
	progress.getCurrentStage().complete();

	var productList = null;
	var taxList = null;
	for (var i = 0; i < input.length && (productList == null || taxList == null); i++) {
		if (input[i].name == args[1]) { // i is product-list
			productList = input.splice(i, 1)[0];
			productList = readProductList(productList.content);
			i--;
		} else if (input[i].name == args[2]) { // i is tax-list
			taxList = input.splice(i, 1)[0];
			taxList = taxList.content.split(';');
			i--;
		}
	}
	if (productList == null) {
		throw 'Product list not found in input files!';
	} else if (taxList == null) {
		throw 'Tax list not found in input files!';
	}
	progress.getCurrentStage().complete();

	var purchases = [];
	var bulletins = [];
	input.forEach(function(i) {
		var x = parseCashdeskLog(progress, productList, taxList, i.content);
		purchases = purchases.concat(x.purchases);
		bulletins.push(x.bulletin);
		progress.getCurrentStage().completeSubstage();
	});
	progress.setData({'purchases': purchases, 'bulletins': bulletins});
	progress.getCurrentStage().complete();

	var result = [{
		'name': 'Einkäufe.csv',
		'content': createCsv(productList, taxList, args[0], purchases)
	}, {
		'name': 'Tagesberichte.csv',
		'content': createCsv(productList, taxList, args[0], bulletins)
	}];
	progress.setData(result);
	progress.getCurrentStage().complete();
}

function parseCashdeskLog(progress, productList, taxList, input) {
	//split input into purchases
	var purchases = [];
	progress.setData(purchases);
	var purchase = null;
	var lines = input.split('\n');
	for (var i = 0; i < lines.length; i++) {
		var match = lines[i].match(/([^"]*;)+"\s*(\d+)-\s*(\d+)-\s*(\d+)\s*\d+\s*(\d+):(\d+)\s*"/);
		if (match) {
			if (purchase != null) {
				purchases.push(purchase);
			}
			purchase = {
				'day': match[2],
				'month': match[3],
				'year': match[4],
				'hour': match[5],
				'minute': match[6],
				'isBulletin': false,
				'lines': []
			};
		} else {
			if (purchase != null) {
				purchase.lines.push(lines[i]);
			}
		}
	}
	progress.getCurrentStage().getCurrentSubstage().completeSubstage();

	// parse purchase lines
	var bulletin = null;
	purchases = purchases.map(function(p) {
		var summaryStartIndex = -1;
		for (var i = 1; i < p.lines.length; i++) {
			if (p.lines[i].indexOf('Gesamt') > -1) {
				summaryStartIndex = i;
				break;
			} else if (p.lines[i].indexOf('Tagesbericht') > -1) {
				p.isBulletin = true;
				break;
			}
		}
		if (summaryStartIndex == -1 && !p.isBulletin) {
			return false;
		}
		p.id = p.lines[0].match(/([^"]*;)+"\s*([^"]+)\s*"/)[2].trim();
		if (p.isBulletin) {
			bulletin = p;
			p.summary = {
				'counter': p.lines[4].match(/([^"]*;)+"\s*Z[^\d]*([^"]*)"/),
				'quantity': p.lines[5].match(/([^"]*;)+"\s*A[^\d]*([^"]*)"/),
				'price': p.lines[6].match(/([^"]*;)+"\s*B[^\d]*([^"]*)"/)
			};
			if (p.summary.counter && p.summary.quantity && p.summary.price) {
				p.summary.counter = p.summary.counter[2];
				p.summary.quantity = p.summary.quantity[2];
				p.summary.price = p.summary.price[2];
			} else {
				throw 'Bulletin not parsable: ' + JSON.stringify(p, null, 2);
			}

			// parse products
			p.products = [];
			var taxesStartIndex = -1;
			for (var i = 8; i < p.lines.length; i++) {
				if (p.lines[i].indexOf('Steuern') > -1) {
					taxesStartIndex = i;
					break;
				}
			}
			for (var i = 8; i < taxesStartIndex; i+=4) {
				var product = {
					'id': p.lines[i].match(/([^"]*;)+"\s*(#\d+)/)[2],
					'name': p.lines[i+1].match(/([^"]*;)+"\s*([^"]*?)\s*"/)[2],
					'quantity': p.lines[i+2].match(/([^"]*;)+"\s*A[^ ]+ +([^"]*)"/)[2],
					'price': p.lines[i+3].match(/([^"]*;)+"\s*B[^ ]+ +([^"]*)"/)[2]
				};
				product.number = parseInt(product.id.substr(1));
				p.products.push(product);
			}
			var paymentMethodsStartIndex = -1;
			for (var i = taxesStartIndex; i < p.lines.length; i++) {
				if (p.lines[i].indexOf('Zahlungsarten') > -1) {
					paymentMethodsStartIndex = i;
					break;
				}
			}
			p.summary.taxes = {};
			for (var i = taxesStartIndex + 1; i < paymentMethodsStartIndex; i+=4) {
				p.summary.taxes[p.lines[i].match(/([^"]*;)+"\s*MwSt\s*([^"]*?)\s*"/)[2]] = p.lines[i+3].match(/([^"]*;)+"\s*MwSt\s*([^"]*)"/)[2];
			}
		} else {
			p.products = [];
			// parse products
			for (var i = 1; i < summaryStartIndex; i++) {
				var product = parseProductLine(p.lines[i]);
				if (product) {
					if (product.quantity[0] === '-') {
						if (p.lines[i-1].indexOf('Retour') > -1) {
							product.note = 'Retour';
						} else if (p.lines[i-1].indexOf('Sofort Storno') > -1) {
							if (p.products.length > 0 && ('-' + p.products[p.products.length - 1].quantity) === product.quantity) {
								p.products.splice(p.products.length - 1, 1);
								continue;
							} else {
								product.note = 'Sofort Storno';
							}
						}
					}
					p.products.push(product);
				}
			}

			// parse summary
			p.summary = parseProductLine(p.lines[summaryStartIndex]);
			delete p.summary.name;
			delete p.summary.note;
			p.summary.taxes = {};
			for (var i = summaryStartIndex + 1; i < p.lines.length; i++) {
				var match = p.lines[i].match(/([^"]*;)+"\s+MwSt\s*(\d+%)\s*(-?\d+\.\d+)/);
				if (match) {
					p.summary.taxes[match[2]] = match[3];
				}
			}
		}
		return p;
	});
	progress.getCurrentStage().getCurrentSubstage().completeSubstage();

	// remove invalid purchases
	purchases = purchases.filter(function(p) {
		return p != false && p.products.length > 0;
	});

	// parse numbers
	purchases.forEach(function(purchase) {
		purchase.products.forEach(function(product) {
			product.price = parseFloat(product.price.replace(/,/g, ''));
			product.quantity = parseFloat(product.quantity.replace(/,/g, ''));
		});
		purchase.summary.quantity = parseInt(purchase.summary.quantity);
		purchase.summary.price = parseFloat(purchase.summary.price.replace(/,/g, ''));
		for (var tax in purchase.summary.taxes) {
			purchase.summary.taxes[tax] = parseFloat(purchase.summary.taxes[tax].replace(/,/g, ''));
		}
	});
	progress.getCurrentStage().getCurrentSubstage().completeSubstage();

	// remove bulletin
	purchases = purchases.filter(function(p) {
		return !p.isBulletin;
	});

	// update product names of purchases
	var unmatchedProductNames = [];
	purchases.forEach(function(purchase) {
		purchase.products.forEach(function(product) {
			var possibleProducts = [];
			bulletin.products.forEach(function(p) {
				if (p.name.indexOf(product.name) > -1) {
					possibleProducts.push(p);
				}
			});
			if (possibleProducts.length === 0) {
				//throw 'No product name matched "' + product.name + '"!';
				var foundName = false;
				for (var i = 0; i < unmatchedProductNames.length; i++) {
					if (unmatchedProductNames[i].indexOf(product.name) > -1 || product.name.indexOf(unmatchedProductNames[i])) {
						product.name = unmatchedProductNames[i];
						foundName = true;
						break;
					}
				}
				if (!foundName) {
					unmatchedProductNames.push(product.name);
				}
				product.note = "Product name not found in bulletin";
			} else if (possibleProducts.length === 1) {
				product.name = productList[possibleProducts[0].number].name;
			} else {
				var pricePerQuantity = product.price / product.quantity;
				var minDelta = Number.POSITIVE_INFINITY;
				var prod = null;
				possibleProducts.forEach(function(p) {
					var delta = Math.abs(pricePerQuantity - p.price / p.quantity);
					if (delta < minDelta) {
						minDelta = delta;
						prod = p;
					}
				});
				product.name = productList[prod.number].name;
			}
		});
	});
	progress.getCurrentStage().getCurrentSubstage().completeSubstage();

	// combine equal products of purchases
	var productCombinations = 0;
	purchases.forEach(function(purchase) {
		for (var i = 0; i < purchase.products.length; i++) {
			for (var j = i + 1; j < purchase.products.length; j++) {
				if (purchase.products[i].name === purchase.products[j].name) {
					productCombinations++;
					var p = purchase.products.splice(j, 1)[0];
					purchase.products[i].price += p.price;
					purchase.products[i].quantity += p.quantity;
					purchase.products[i].note = 'Kombiniert';
					j--;
				}
			}
		}
	});
	progress.getCurrentStage().getCurrentSubstage().completeSubstage();

	// update bulletin product names
	bulletin.products.forEach(function(product) {
		product.name = productList[product.number].name;
	});

	return {
		'bulletin': bulletin,
		'purchases': purchases
	};
}

function parseProductLine(line) {
	var match = line.match(/([^"]*;)+"(-?\d*(\.\d+)?)\s+(.*?)\s+(-?\d+\.\d+)/);
	if (match) {
		return {
			'quantity': match[2],
			'name': match[4],
			'price': match[5],
			'note': ''
		};
	} else {
		return false;
	}
}

function createCsvHeader(productList, taxList) {
	var csv = 'RechnungsNr;Tag;Monat;Jahr;Stunde;Minute;Gesamtpreis';
	taxList.forEach(function(tax) {
		csv += ';' + tax + ' MwSt Anteil';
	});
	for (var i in productList) {
		csv += ';' + productList[i].name + ' Menge;' + productList[i].name + ' Preis';
	};
	return csv;
}

function createCsv(productList, taxList, decimalMark, items) {
	var csv = '';
	items.forEach(function(item) {
		csv += '\n';
		csv += [item.id, item.day, item.month, item.year, item.hour, item.minute, item.summary.price].join(';');
		taxList.forEach(function(tax) {
			csv += ';';
			if (item.summary.taxes[tax] != undefined) {
				csv += item.summary.taxes[tax];
			} else {
				csv += '0';
			}
		});
		for (var pIndex in productList) {
			csv += ';';
			var hasProduct = false;
			for (var i = 0; i < item.products.length; i++) {
				if (productList[pIndex].name === item.products[i].name) {
					csv += [item.products[i].quantity, item.products[i].price].join(';');
					hasProduct = true;
					break;
				}
			}
			if (!hasProduct) {
				csv += '0;0';
			}
		}
	});
	return createCsvHeader(productList, taxList) + csv.replace(/\./g, decimalMark);
}

function readProductList(input) {
	var lines = input.split('\n');
	var productList = [];
	for (var i = 1; i < lines.length; i++) {
		var spl = lines[i].split(';');
		var product = {
			'number': parseFloat(spl[0].trim()),
			'name': spl[1].trim()
		};
		productList[product.number] = product;
	}
	return productList;
}
<|endoftext|>


<commit_before>var currentPlaylistIndex = -1;
var currentVideolistIndex = -1;

var playerReady = false;

var replayVideo = false;

var hideControlsTimeout;

var playerUnstartedTimer;

// Hide cursor after a period of inactivity
var cursorHidden = false;
var hideCursorTimer;

var progressTimer;
var currentVidUrl;

var PLAYLIST_DATA_NAME_INDEX  = 0;
var PLAYLIST_DATA_SONGS_INDEX = 1;

var WIDTH = 0;
var HEIGHT = 1;

function getPlayer() {
    return document.getElementById("player");
}

function calculatePlayerSize() {
    var w = $(window).width() - 80;
    var h = $(window).height() - 50;

    if ($("#playlists-container").is(':visible')) {
        w = w - $("#playlists-container").width();
    }

    return [w, h];
}

function initPlayer() {
    var swfUrl = 'http://www.youtube.com/apiplayer?enablejsapi=1&modestbranding=1&version=3&playerapiid=player';

    // allowScriptAccess must be set to allow the Javascript from one
    // domain to access the swf on the youtube domain
    var params = { allowScriptAccess: "always", bgcolor: "#ffffff" };

    var flashvars = {};

    var playerSize = calculatePlayerSize();

    // This sets the ID of the DOM object or embed tag to 'player'.
    // You can use this ID to access the swf and call the player's API
    var atts = { id: "player" }; // 640 x 360
    swfobject.embedSWF(swfUrl, "player-placeholder", playerSize[WIDTH], playerSize[HEIGHT], "9", null, flashvars, params, atts);

    // Init the volume slider
    $("#volume-slider").noUiSlider({
        start: 50,
        step: 1,
        orientation: "vertical",
        direction: "rtl",
        connect: "lower",
        range: {
          'min': [   0 ],
          'max': [ 100 ]
        }
    });
}

function getVideolist(playlistIndex) {
    return allPlaylists[playlistIndex][PLAYLIST_DATA_SONGS_INDEX];
}

function showPlaylists() {
    hideControls();

    $("#playlist").empty();
    for (var i = 0; i < allPlaylists.length; i++) {
        $("#playlist").append("
" + allPlaylists[i][PLAYLIST_DATA_NAME_INDEX] + "
");
    }
    $("#playlist li:nth-child(" + (currentPlaylistIndex + 1) + ")").addClass("playing");

    $("#playlist li").hover(function() {
        $(this).addClass("hovering");
    }, function() {
        $(this).removeClass("hovering");
    });

    showVideolist(currentPlaylistIndex);

    resizePlaylists();
    $("#playlists-container").show();
}

function showVideolist(playlistIndex) {
    $("#playlist li").removeClass("selected");
    $("#playlist li:nth-child(" + (playlistIndex + 1) + ")").addClass("selected");

    $("#videolist").empty();

    var videolist = getVideolist(playlistIndex);
    for (var j = 0; j < videolist.length; j++) {
        $("#videolist").append("
" + videolist[j]["title"] + "
" + videolist[j]["artist"] + "
");
    }

    $("#videolist li").hover(function() {
        $(this).addClass("hovering");
    }, function() {
        $(this).removeClass("hovering");
    });

    if (playlistIndex == currentPlaylistIndex) {
        $("#videolist li:nth-child(" + (currentVideolistIndex + 1) + ")").addClass("playing");
    }
}

function hidePlaylists() {
    $("#playlists-container").hide();
}

function beginningVideo() {
    loadVideo(currentPlaylistIndex, currentVideolistIndex);
}

function previousVideo() {
    var previousPlaylistIndex = currentPlaylistIndex;
    var previousVideolistIndex = currentVideolistIndex;

    previousVideolistIndex = previousVideolistIndex - 1;

    if (previousVideolistIndex < 0) {
        previousPlaylistIndex = previousPlaylistIndex - 1;
        if (previousPlaylistIndex < 0) {
            previousPlaylistIndex = allPlaylists.length - 1;
        }

        previousVideolistIndex = getVideolist(previousPlaylistIndex).length - 1;
    }

    loadVideo(previousPlaylistIndex, previousVideolistIndex);
}

function nextVideo() {
    var nextPlaylistIndex = currentPlaylistIndex;
    var nextVideolistIndex = currentVideolistIndex;

    nextVideolistIndex = nextVideolistIndex + 1

    if (nextVideolistIndex >= getVideolist(nextPlaylistIndex).length) {
        nextVideolistIndex = 0;

        nextPlaylistIndex = nextPlaylistIndex + 1;
        if (nextPlaylistIndex >= allPlaylists.length) {
            nextPlaylistIndex = 0;
        }
    }

    loadVideo(nextPlaylistIndex, nextVideolistIndex);
}

function loadVideo(playlistIndex, videolistIndex) {
    hidePlaylists();

    var videoData = getVideolist(playlistIndex)[videolistIndex];
    var start = videoData["start"] || 0;
    var end = videoData["end"];

    if (end == null) {
        getPlayer().loadVideoById(videoData["vid"], start);
    } else {
        getPlayer().loadVideoById(videoData["vid"], start, end);
    }

    currentPlaylistIndex  = playlistIndex;
    currentVideolistIndex = videolistIndex;

    var uri = new Uri(window.location.href);
    uri.replaceQueryParam("pl", currentPlaylistIndex.toString());
    uri.replaceQueryParam("vl", currentVideolistIndex.toString());

    history.replaceState(null, null, uri.toString());
}

function downloadVideo() {
    var videoData = getVideolist(currentPlaylistIndex)[currentVideolistIndex];
    window.open("http://ssyoutube.com/watch?v=" + videoData["vid"]);
}

function onYouTubePlayerReady(playerId) {
  	console.log("Player is ready! [" + playerId + "]");

  	getPlayer().addEventListener("onStateChange", "onYouTubePlayerEvent");

    if (!playerReady) {
        $(document).mousemove(showCursor);
        $(document).mousemove(showControls);

        $("#player-overlay").click(playerClick);

  	    $("#show-playlists").click(showPlaylists);
        $("#hide-playlists").click(hidePlaylists);

        $("#pause").click(pauseVideo);
        $("#play").click(playVideo);
        $("#replay").click(toggleReplayVideo);
        $("#beginning").click(beginningVideo);
        $("#previous").click(previousVideo);
        $("#next").click(nextVideo);
        $("#mute").click(mute);
        $("#unmute").click(unmute);
        $("#volume-slider").on("slide", volumeChange);
        $("#download").click(downloadVideo);

        playerReady = true;
    }

    var uri = new Uri(window.location.href);
    var playlistIndex = uri.getQueryParamValue("pl") || 0;
    var videolistIndex = uri.getQueryParamValue("vl") || 0;

    loadVideo(parseInt(playlistIndex), parseInt(videolistIndex));
}

function hideCursor() {
    $("html").css({cursor: "url(images/transparent.png), default"});
    cursorHidden = true;
    hideCursorTimer = null;
    hideProgress();
}

function showCursor() {
    clearTimeout(hideCursorTimer);
    if (cursorHidden) {
        $("html").css({cursor: "default"});
        cursorHidden = false;
    }
    hideCursorTimer = setTimeout('hideCursor()', 5000);
    showProgress();
}

function hideControls() {
    $("#controls-container").hide();
}

function showControls(event) {
    if ((!$("#playlists-container").is(':visible') && event.pageX < 20) || $("#controls-container").is(':hover')) {
        // Show the controls if mouse is within 20 pixels from the left or we are hovering over it
        clearTimeout(hideControlsTimeout);
        hideControlsTimeout = null;
        $("#controls-container").show();
    } else if (!hideControlsTimeout) {
        hideControlsTimeout = setTimeout(hideControls, 2000);
    }
}

function pauseVideo() {
    getPlayer().pauseVideo();
}

function playVideo() {
    getPlayer().playVideo();
}

function toggleReplayVideo() {
    if (replayVideo) {
        $("#replay").attr("src", "images/replay.png");
        replayVideo = false;
    } else {
        $("#replay").attr("src", "images/replay-active.png");
        replayVideo = true;
    }
}

function mute() {
    getPlayer().mute();
    $("#mute").hide();
    $("#unmute").show();
    $("#volume").hide();
}

function unmute() {
    getPlayer().unMute();
    $("#unmute").hide();
    $("#mute").show();
    $("#volume").show();
}

function volumeChange() {
    var volume = parseInt($("#volume-slider").val());
    getPlayer().setVolume(volume);
    $("#volume-label").html(volume + "%");
}

function playerClick() {
    if ($("#pause").is(":visible")) {
        pauseVideo();
    } else {
        playVideo();
    }
}

function showProgress() {
    $("#progress-slider-container").show();
}

function hideProgress() {
    $("#progress-slider-container").hide();
}

function formatProgressTime(time) {
    var minutes = Math.floor(time / 60);
    var seconds = time - minutes * 60;

    var formattedTime = "";
    if (minutes > 0) {
        formattedTime = minutes + ":";
        if (seconds < 10) {
            formattedTime = formattedTime + "0";
        }
    }

    formattedTime = formattedTime + seconds;

    return formattedTime;
}

function updateProgress() {
    var player = getPlayer();
    var duration = parseInt(player.getDuration());
    var currentTime = parseInt(player.getCurrentTime());

    $("#progress-slider").val(currentTime);
    $("#progress-time").html(formatProgressTime(currentTime) + " / " + formatProgressTime(duration));
}

function changeProgress() {
    var progress = parseInt($("#progress-slider").val());
    getPlayer().seekTo(progress, true);
}

function startProgress() {
    $("#progress-slider-container").append("

");
    $("#progress-slider").noUiSlider({
            start: 0,
            step: 1,
            connect: "lower",
            range: {
              "min": [   0 ],
              "max": [ player.getDuration() ]
            }
    });
    $("#progress-slider").on("slide", changeProgress);
    progressTimer = setInterval(updateProgress, 1000);
}

function stopProgress() {
    clearTimeout(progressTimer);
    $("#progress-slider-container").empty();
    progressTimer = null;
}

function stopUnstarted() {
    clearTimeout(playerUnstartedTimer);
    playerUnstartedTimer = null;
}

// Called when a video has not started after a given time
function unstarted() {
    stopUnstarted();
    nextVideo();
}

function onYouTubePlayerEvent(event) {
    console.log("player event [" + event + "]");

    if (event == YT.PlayerState.PLAYING) {
        stopUnstarted();

        var videoData = getVideolist(currentPlaylistIndex)[currentVideolistIndex];
        $("#title").html(videoData["title"]);
        $("#artist").html(videoData["artist"]);

        if ($("#playlists-container").is(":visible")) {
            $("#playlist li").removeClass("playing");
            $("#playlist li:nth-child(" + (currentPlaylistIndex + 1) + ")").addClass("playing");
            if ($("#playlist li:nth-child(" + (currentPlaylistIndex + 1) + ")").hasClass("selected")) {
                $("#videolist li").removeClass("playing");
                $("#videolist li:nth-child(" + (currentVideolistIndex + 1) + ")").addClass("playing");
            }
        }

        var vidUrl = getPlayer().getVideoUrl();
        if (vidUrl != currentVidUrl) {
            stopProgress();
            startProgress(vidUrl);
            currentVidUrl = vidUrl;
        }
    } else if (event == YT.PlayerState.ENDED) { // end of a playlist
        stopProgress();
        if (replayVideo) {
            currentVidUrl = null;
            loadVideo(currentPlaylistIndex, currentVideolistIndex);
        } else {
            nextVideo();
        }
    } else if (event == -1) {
        stopUnstarted();
        stopProgress();
        playerUnstartedTimer = setTimeout('unstarted()', 7000);
    }

    manageControls(event);
}

function manageControls(event) {
    if (event == YT.PlayerState.PLAYING) {
        $("#play").hide();
        $("#pause").show();
        $("#replay").show();
        $("#beginning").show();
        $("#previous").show();
        $("#next").show();
        $("#download").show();

        var player = getPlayer();
        if (player.isMuted()) {
            $("#mute").hide();
            $("#unmute").show();
            $("#volume").hide();
        } else {
            $("#mute").show();
            $("#unmute").hide();
            var volume = player.getVolume();
            $("#volume-slider").val(volume);
            $("#volume-label").html(volume + "%");
            $("#volume").show();
        }
    } else if (event == YT.PlayerState.PAUSED) {
        $("#play").show();
        $("#pause").hide();
        $("#replay").show();
        $("#beginning").show();
        $("#previous").show();
        $("#next").show();
        $("#download").show();

        if (getPlayer().isMuted()) {
            $("#mute").hide();
            $("#unmute").show();
        } else {
            $("#mute").show();
            $("#unmute").hide();
        }
    } else {
        $("#play").hide();
        $("#pause").hide();
        $("#replay").hide();
        $("#beginning").hide();
        $("#previous").hide();
        $("#next").hide();
        $("#mute").hide();
        $("#unmute").hide();
        $("#volume").hide();
        $("#download").hide();
    }
}

function resizePlayer() {
    var player = $("#player");
    if (player.size() > 0) {
        var playerSize = calculatePlayerSize();
        player.animate({width: playerSize[WIDTH], height: playerSize[HEIGHT]}, 150, "swing", resizePlaylists);
    }
}

function resizePlaylists() {
    var height = $("#player-container").height();
    $("#playlists-container").css("height", height);
    $("#playlist-container").css("height", height);
    $("#videolist-container").css("height", height);
}

$(window).on("resize", function() {
    resizePlayer();
});

$(document).ready(function() {
    initPlayer();
});
<commit_msg>Show controls when the cursor moves<commit_after>var currentPlaylistIndex = -1;
var currentVideolistIndex = -1;

var playerReady = false;

var replayVideo = false;

var playerUnstartedTimer;

// Hide cursor after a period of inactivity
var cursorHidden = false;
var hideCursorTimer;

var progressTimer;
var currentVidUrl;

var PLAYLIST_DATA_NAME_INDEX  = 0;
var PLAYLIST_DATA_SONGS_INDEX = 1;

var WIDTH = 0;
var HEIGHT = 1;

function getPlayer() {
    return document.getElementById("player");
}

function calculatePlayerSize() {
    var w = $(window).width() - 80;
    var h = $(window).height() - 50;

    if ($("#playlists-container").is(':visible')) {
        w = w - $("#playlists-container").width();
    }

    return [w, h];
}

function initPlayer() {
    var swfUrl = 'http://www.youtube.com/apiplayer?enablejsapi=1&modestbranding=1&version=3&playerapiid=player';

    // allowScriptAccess must be set to allow the Javascript from one
    // domain to access the swf on the youtube domain
    var params = { allowScriptAccess: "always", bgcolor: "#ffffff" };

    var flashvars = {};

    var playerSize = calculatePlayerSize();

    // This sets the ID of the DOM object or embed tag to 'player'.
    // You can use this ID to access the swf and call the player's API
    var atts = { id: "player" }; // 640 x 360
    swfobject.embedSWF(swfUrl, "player-placeholder", playerSize[WIDTH], playerSize[HEIGHT], "9", null, flashvars, params, atts);

    // Init the volume slider
    $("#volume-slider").noUiSlider({
        start: 50,
        step: 1,
        orientation: "vertical",
        direction: "rtl",
        connect: "lower",
        range: {
          'min': [   0 ],
          'max': [ 100 ]
        }
    });
}

function getVideolist(playlistIndex) {
    return allPlaylists[playlistIndex][PLAYLIST_DATA_SONGS_INDEX];
}

function showPlaylists() {
    hideControls();
    hideProgress();

    $("#playlist").empty();
    for (var i = 0; i < allPlaylists.length; i++) {
        $("#playlist").append("
" + allPlaylists[i][PLAYLIST_DATA_NAME_INDEX] + "
");
    }
    $("#playlist li:nth-child(" + (currentPlaylistIndex + 1) + ")").addClass("playing");

    $("#playlist li").hover(function() {
        $(this).addClass("hovering");
    }, function() {
        $(this).removeClass("hovering");
    });

    showVideolist(currentPlaylistIndex);

    resizePlaylists();
    $("#playlists-container").show();
}

function showVideolist(playlistIndex) {
    $("#playlist li").removeClass("selected");
    $("#playlist li:nth-child(" + (playlistIndex + 1) + ")").addClass("selected");

    $("#videolist").empty();

    var videolist = getVideolist(playlistIndex);
    for (var j = 0; j < videolist.length; j++) {
        $("#videolist").append("
" + videolist[j]["title"] + "
" + videolist[j]["artist"] + "
");
    }

    $("#videolist li").hover(function() {
        $(this).addClass("hovering");
    }, function() {
        $(this).removeClass("hovering");
    });

    if (playlistIndex == currentPlaylistIndex) {
        $("#videolist li:nth-child(" + (currentVideolistIndex + 1) + ")").addClass("playing");
    }
}

function hidePlaylists() {
    $("#playlists-container").hide();
}

function beginningVideo() {
    loadVideo(currentPlaylistIndex, currentVideolistIndex);
}

function previousVideo() {
    var previousPlaylistIndex = currentPlaylistIndex;
    var previousVideolistIndex = currentVideolistIndex;

    previousVideolistIndex = previousVideolistIndex - 1;

    if (previousVideolistIndex < 0) {
        previousPlaylistIndex = previousPlaylistIndex - 1;
        if (previousPlaylistIndex < 0) {
            previousPlaylistIndex = allPlaylists.length - 1;
        }

        previousVideolistIndex = getVideolist(previousPlaylistIndex).length - 1;
    }

    loadVideo(previousPlaylistIndex, previousVideolistIndex);
}

function nextVideo() {
    var nextPlaylistIndex = currentPlaylistIndex;
    var nextVideolistIndex = currentVideolistIndex;

    nextVideolistIndex = nextVideolistIndex + 1

    if (nextVideolistIndex >= getVideolist(nextPlaylistIndex).length) {
        nextVideolistIndex = 0;

        nextPlaylistIndex = nextPlaylistIndex + 1;
        if (nextPlaylistIndex >= allPlaylists.length) {
            nextPlaylistIndex = 0;
        }
    }

    loadVideo(nextPlaylistIndex, nextVideolistIndex);
}

function loadVideo(playlistIndex, videolistIndex) {
    hidePlaylists();

    var videoData = getVideolist(playlistIndex)[videolistIndex];
    var start = videoData["start"] || 0;
    var end = videoData["end"];

    if (end == null) {
        getPlayer().loadVideoById(videoData["vid"], start);
    } else {
        getPlayer().loadVideoById(videoData["vid"], start, end);
    }

    currentPlaylistIndex  = playlistIndex;
    currentVideolistIndex = videolistIndex;

    var uri = new Uri(window.location.href);
    uri.replaceQueryParam("pl", currentPlaylistIndex.toString());
    uri.replaceQueryParam("vl", currentVideolistIndex.toString());

    history.replaceState(null, null, uri.toString());
}

function downloadVideo() {
    var videoData = getVideolist(currentPlaylistIndex)[currentVideolistIndex];
    window.open("http://ssyoutube.com/watch?v=" + videoData["vid"]);
}

function onYouTubePlayerReady(playerId) {
  	console.log("Player is ready! [" + playerId + "]");

  	getPlayer().addEventListener("onStateChange", "onYouTubePlayerEvent");

    if (!playerReady) {
        $(document).mousemove(showCursor);
        $(document).mousemove(showControls);

        $("#player-overlay").click(playerClick);

  	    $("#show-playlists").click(showPlaylists);
        $("#hide-playlists").click(hidePlaylists);

        $("#pause").click(pauseVideo);
        $("#play").click(playVideo);
        $("#replay").click(toggleReplayVideo);
        $("#beginning").click(beginningVideo);
        $("#previous").click(previousVideo);
        $("#next").click(nextVideo);
        $("#mute").click(mute);
        $("#unmute").click(unmute);
        $("#volume-slider").on("slide", volumeChange);
        $("#download").click(downloadVideo);

        playerReady = true;
    }

    var uri = new Uri(window.location.href);
    var playlistIndex = uri.getQueryParamValue("pl") || 0;
    var videolistIndex = uri.getQueryParamValue("vl") || 0;

    loadVideo(parseInt(playlistIndex), parseInt(videolistIndex));
}

function hideCursor() {
    $("html").css({cursor: "url(images/transparent.png), default"});
    cursorHidden = true;
    hideCursorTimer = null;
    hideControls();
    hideProgress();
}

function showCursor() {
    clearTimeout(hideCursorTimer);
    if (cursorHidden) {
        $("html").css({cursor: "default"});
        cursorHidden = false;
    }
    hideCursorTimer = setTimeout('hideCursor()', 5000);
    showControls();
    showProgress();
}

function hideControls() {
    $("#controls-container").hide();
}

function showControls(event) {
    if (!$("#playlists-container").is(':visible')) {
        $("#controls-container").show();
    }
}

function pauseVideo() {
    getPlayer().pauseVideo();
}

function playVideo() {
    getPlayer().playVideo();
}

function toggleReplayVideo() {
    if (replayVideo) {
        $("#replay").attr("src", "images/replay.png");
        replayVideo = false;
    } else {
        $("#replay").attr("src", "images/replay-active.png");
        replayVideo = true;
    }
}

function mute() {
    getPlayer().mute();
    $("#mute").hide();
    $("#unmute").show();
    $("#volume").hide();
}

function unmute() {
    getPlayer().unMute();
    $("#unmute").hide();
    $("#mute").show();
    $("#volume").show();
}

function volumeChange() {
    var volume = parseInt($("#volume-slider").val());
    getPlayer().setVolume(volume);
    $("#volume-label").html(volume + "%");
}

function playerClick() {
    if ($("#pause").is(":visible")) {
        pauseVideo();
    } else {
        playVideo();
    }
}

function showProgress() {
    if (!$("#playlists-container").is(':visible')) {
        $("#progress-slider-container").show();
    }
}

function hideProgress() {
    $("#progress-slider-container").hide();
}

function formatProgressTime(time) {
    var minutes = Math.floor(time / 60);
    var seconds = time - minutes * 60;

    var formattedTime = "";
    if (minutes > 0) {
        formattedTime = minutes + ":";
        if (seconds < 10) {
            formattedTime = formattedTime + "0";
        }
    }

    formattedTime = formattedTime + seconds;

    return formattedTime;
}

function updateProgress() {
    var player = getPlayer();
    var duration = parseInt(player.getDuration());
    var currentTime = parseInt(player.getCurrentTime());

    $("#progress-slider").val(currentTime);
    $("#progress-time").html(formatProgressTime(currentTime) + " / " + formatProgressTime(duration));
}

function changeProgress() {
    var progress = parseInt($("#progress-slider").val());
    getPlayer().seekTo(progress, true);
}

function startProgress() {
    $("#progress-slider-container").append("

");
    $("#progress-slider").noUiSlider({
            start: 0,
            step: 1,
            connect: "lower",
            range: {
              "min": [   0 ],
              "max": [ player.getDuration() ]
            }
    });
    $("#progress-slider").on("slide", changeProgress);
    progressTimer = setInterval(updateProgress, 1000);
}

function stopProgress() {
    clearTimeout(progressTimer);
    $("#progress-slider-container").empty();
    progressTimer = null;
}

function stopUnstarted() {
    clearTimeout(playerUnstartedTimer);
    playerUnstartedTimer = null;
}

// Called when a video has not started after a given time
function unstarted() {
    stopUnstarted();
    nextVideo();
}

function onYouTubePlayerEvent(event) {
    console.log("player event [" + event + "]");

    if (event == YT.PlayerState.PLAYING) {
        stopUnstarted();

        var videoData = getVideolist(currentPlaylistIndex)[currentVideolistIndex];
        $("#title").html(videoData["title"]);
        $("#artist").html(videoData["artist"]);

        if ($("#playlists-container").is(":visible")) {
            $("#playlist li").removeClass("playing");
            $("#playlist li:nth-child(" + (currentPlaylistIndex + 1) + ")").addClass("playing");
            if ($("#playlist li:nth-child(" + (currentPlaylistIndex + 1) + ")").hasClass("selected")) {
                $("#videolist li").removeClass("playing");
                $("#videolist li:nth-child(" + (currentVideolistIndex + 1) + ")").addClass("playing");
            }
        }

        var vidUrl = getPlayer().getVideoUrl();
        if (vidUrl != currentVidUrl) {
            stopProgress();
            startProgress(vidUrl);
            currentVidUrl = vidUrl;
        }
    } else if (event == YT.PlayerState.ENDED) { // end of a playlist
        stopProgress();
        if (replayVideo) {
            currentVidUrl = null;
            loadVideo(currentPlaylistIndex, currentVideolistIndex);
        } else {
            nextVideo();
        }
    } else if (event == -1) {
        stopUnstarted();
        stopProgress();
        playerUnstartedTimer = setTimeout('unstarted()', 7000);
    }

    manageControls(event);
}

function manageControls(event) {
    if (event == YT.PlayerState.PLAYING) {
        $("#play").hide();
        $("#pause").show();
        $("#replay").show();
        $("#beginning").show();
        $("#previous").show();
        $("#next").show();
        $("#download").show();

        var player = getPlayer();
        if (player.isMuted()) {
            $("#mute").hide();
            $("#unmute").show();
            $("#volume").hide();
        } else {
            $("#mute").show();
            $("#unmute").hide();
            var volume = player.getVolume();
            $("#volume-slider").val(volume);
            $("#volume-label").html(volume + "%");
            $("#volume").show();
        }
    } else if (event == YT.PlayerState.PAUSED) {
        $("#play").show();
        $("#pause").hide();
        $("#replay").show();
        $("#beginning").show();
        $("#previous").show();
        $("#next").show();
        $("#download").show();

        if (getPlayer().isMuted()) {
            $("#mute").hide();
            $("#unmute").show();
        } else {
            $("#mute").show();
            $("#unmute").hide();
        }
    } else {
        $("#play").hide();
        $("#pause").hide();
        $("#replay").hide();
        $("#beginning").hide();
        $("#previous").hide();
        $("#next").hide();
        $("#mute").hide();
        $("#unmute").hide();
        $("#volume").hide();
        $("#download").hide();
    }
}

function resizePlayer() {
    var player = $("#player");
    if (player.size() > 0) {
        var playerSize = calculatePlayerSize();
        player.animate({width: playerSize[WIDTH], height: playerSize[HEIGHT]}, 150, "swing", resizePlaylists);
    }
}

function resizePlaylists() {
    var height = $("#player-container").height();
    $("#playlists-container").css("height", height);
    $("#playlist-container").css("height", height);
    $("#videolist-container").css("height", height);
}

$(window).on("resize", function() {
    resizePlayer();
});

$(document).ready(function() {
    initPlayer();
});
<|endoftext|>


<commit_before>/* @license minigrid v2.2.0 – minimal cascading grid layout http://alves.im/minigrid */
(function (exports) {

  'use strict';

  var transformProp;
  var loaded;

  (function () {
    var style = document.createElement('a').style;
    var prop;
    if (style[prop = 'webkitTransform'] !== undefined) {
      transformProp = prop;
    }
    if (style[prop = 'msTransform'] !== undefined) {
      transformProp = prop;
    }
    if (style[prop = 'transform'] !== undefined) {
      transformProp = prop;
    }
  }());

  function minigrid(props) {
    var containerEle = props.container instanceof Node ?
      props.container : document.querySelector(props.container);

    if (!containerEle) {
      return false;
    }

    var itemsNodeList = props.item instanceof NodeList ?
      props.item : containerEle.querySelectorAll(props.item);
    if (!itemsNodeList || itemsNodeList.length === 0) {
      return false;
    }

    if (!props.containerLoaded || typeof props.containerLoaded !== 'string') {
      props.containerLoaded = false;
    }

    if (!props.containerLoaded || typeof props.itemLoaded !== 'string') {
      props.itemLoaded = false;
    }

    if (loaded || props.skipWindowOnLoad) {
      init(containerEle, itemsNodeList, props);
      return;
    }

    if (/webkit/.test(navigator.userAgent.toLowerCase())) {
      setTimeout(function() {
        webkitWaitForReadyState({
          container: containerEle,
          item: itemsNodeList,
          props: props
        });
      }, 10);
    } else {
      window.onload = function() {
        init(containerEle, itemsNodeList, props);
      };
    }

  }

  function webkitWaitForReadyState(minigrid) {
    if (document.readyState === 'loaded' || document.readyState === 'complete') {
      init(minigrid.container, minigrid.item, minigrid.props);
    } else {
      setTimeout(function() {
        webkitWaitForReadyState(minigrid);
      }, 10);
    }
  }

  function init(containerEle, itemsNodeList, props) {
    if (props.containerLoaded) {
      containerEle.classList.add(props.containerLoaded);
    } else if (!/loaded/.test(containerEle.className)) {
      containerEle.classList.add(containerEle.className.split(' ')[0] + '--loaded');
    }

    loaded = true;

    var gutter = (
      typeof props.gutter === 'number' &&
      isFinite(props.gutter) &&
      Math.floor(props.gutter) === props.gutter
    ) ? props.gutter : 0;
    var done = props.done;

    containerEle.style.width = '';

    var forEach = Array.prototype.forEach;
    var containerWidth = containerEle.getBoundingClientRect().width;
    var firstChildWidth = itemsNodeList[0].getBoundingClientRect().width + gutter;
    var cols = Math.max(Math.floor((containerWidth - gutter) / firstChildWidth), 1);
    var count = 0;

    containerWidth = (firstChildWidth * cols + gutter) + 'px';
    containerEle.style.width = containerWidth;
    containerEle.style.position = 'relative';

    var itemsGutter = [];
    var itemsPosX = [];

    for ( var g = 0 ; g < cols ; ++g ) {
      itemsPosX.push(g * firstChildWidth + gutter);
      itemsGutter.push(gutter);
    }

    forEach.call(itemsNodeList, function (item) {
      var itemIndex = itemsGutter
        .slice(0)
        .sort(function (a, b) {
          return a - b;
        })
        .shift();
      itemIndex = itemsGutter.indexOf(itemIndex);

      var posX = itemsPosX[itemIndex];
      var posY = itemsGutter[itemIndex];

      item.style.position = 'absolute';
      if (props.itemLoaded) {
        item.classList.add(props.itemLoaded);
      } else if (!/loaded/.test(item.className)) {
        item.classList.add(item.className.split(' ')[0] + '--loaded');
      }

      if (!props.animate && transformProp) {
        item.style[transformProp] = 'translate3D(' + posX + 'px,' + posY + 'px, 0)';
      }

      itemsGutter[itemIndex] += item.getBoundingClientRect().height + gutter;
      count = count + 1;

      if (props.animate) {
        return props.animate(item, posX, posY, count);
      }

    });

    var containerHeight = itemsGutter
      .slice(0)
      .sort(function (a, b) {
        return a - b;
      })
      .pop();

    containerEle.style.height = containerHeight + 'px';

    if (typeof done === 'function') {
      done(itemsNodeList);
    }
  }

  if (typeof define === 'function' && define.amd) {
    define(function() { return minigrid; });
  } else if (typeof module !== 'undefined' && module.exports) {
    module.exports = minigrid;
  } else {
    exports.minigrid = minigrid;
  }

})(this);
<commit_msg>Remove custom window.onLoad #36<commit_after>/* @license minigrid v2.2.0 – minimal cascading grid layout http://alves.im/minigrid */
(function (exports) {

  'use strict';

  var transformProp;
  var loaded;

  (function () {
    var style = document.createElement('a').style;
    var prop;
    if (style[prop = 'webkitTransform'] !== undefined) {
      transformProp = prop;
    }
    if (style[prop = 'msTransform'] !== undefined) {
      transformProp = prop;
    }
    if (style[prop = 'transform'] !== undefined) {
      transformProp = prop;
    }
  }());

  function minigrid(props) {
    var containerEle = props.container instanceof Node ?
      props.container : document.querySelector(props.container);

    if (!containerEle) {
      return false;
    }

    var itemsNodeList = props.item instanceof NodeList ?
      props.item : containerEle.querySelectorAll(props.item);
    if (!itemsNodeList || itemsNodeList.length === 0) {
      return false;
    }

    if (!props.containerLoaded || typeof props.containerLoaded !== 'string') {
      props.containerLoaded = false;
    }

    if (!props.containerLoaded || typeof props.itemLoaded !== 'string') {
      props.itemLoaded = false;
    }

    if (loaded || props.skipWindowOnLoad) {
      init(containerEle, itemsNodeList, props);
      return;
    }

    window.addEventListener('load', function(){
      init(containerEle, itemsNodeList, props);
    });

  }

  function init(containerEle, itemsNodeList, props) {
    if (props.containerLoaded) {
      containerEle.classList.add(props.containerLoaded);
    } else if (!/loaded/.test(containerEle.className)) {
      containerEle.classList.add(containerEle.className.split(' ')[0] + '--loaded');
    }

    loaded = true;

    var gutter = (
      typeof props.gutter === 'number' &&
      isFinite(props.gutter) &&
      Math.floor(props.gutter) === props.gutter
    ) ? props.gutter : 0;
    var done = props.done;

    containerEle.style.width = '';

    var forEach = Array.prototype.forEach;
    var containerWidth = containerEle.getBoundingClientRect().width;
    var firstChildWidth = itemsNodeList[0].getBoundingClientRect().width + gutter;
    var cols = Math.max(Math.floor((containerWidth - gutter) / firstChildWidth), 1);
    var count = 0;

    containerWidth = (firstChildWidth * cols + gutter) + 'px';
    containerEle.style.width = containerWidth;
    containerEle.style.position = 'relative';

    var itemsGutter = [];
    var itemsPosX = [];

    for ( var g = 0 ; g < cols ; ++g ) {
      itemsPosX.push(g * firstChildWidth + gutter);
      itemsGutter.push(gutter);
    }

    forEach.call(itemsNodeList, function (item) {
      var itemIndex = itemsGutter
        .slice(0)
        .sort(function (a, b) {
          return a - b;
        })
        .shift();
      itemIndex = itemsGutter.indexOf(itemIndex);

      var posX = itemsPosX[itemIndex];
      var posY = itemsGutter[itemIndex];

      item.style.position = 'absolute';
      if (props.itemLoaded) {
        item.classList.add(props.itemLoaded);
      } else if (!/loaded/.test(item.className)) {
        item.classList.add(item.className.split(' ')[0] + '--loaded');
      }

      if (!props.animate && transformProp) {
        item.style[transformProp] = 'translate3D(' + posX + 'px,' + posY + 'px, 0)';
      }

      itemsGutter[itemIndex] += item.getBoundingClientRect().height + gutter;
      count = count + 1;

      if (props.animate) {
        return props.animate(item, posX, posY, count);
      }

    });

    var containerHeight = itemsGutter
      .slice(0)
      .sort(function (a, b) {
        return a - b;
      })
      .pop();

    containerEle.style.height = containerHeight + 'px';

    if (typeof done === 'function') {
      done(itemsNodeList);
    }
  }

  if (typeof define === 'function' && define.amd) {
    define(function() { return minigrid; });
  } else if (typeof module !== 'undefined' && module.exports) {
    module.exports = minigrid;
  } else {
    exports.minigrid = minigrid;
  }

})(this);
<|endoftext|>


<commit_before>$(function(){

	var filemanager = $('.filemanager'),
		breadcrumbs = $('.breadcrumbs'),
		fileList = filemanager.find('.data');

	// Start by fetching the file data from scan route with an AJAX request

	$.get('scan', function(data) {

		var response = [data],
			currentPath = '',
			breadcrumbsUrls = [];

		var folders = [],
			files = [];

		// This event listener monitors changes on the URL. We use it to
		// capture back/forward navigation in the browser.

		$(window).on('hashchange', function(){

			goto(window.location.hash);

			// We are triggering the event. This will execute
			// this function on page load, so that we show the correct folder:

		}).trigger('hashchange');

		// Hiding and showing the search box

		filemanager.find('.search').click(function(){

			var search = $(this);

			search.find('span').hide();
			search.find('input[type=search]').show().focus();

		});

		// Listening for keyboard input on the search field.
		// We are using the "input" event which detects cut and paste
		// in addition to keyboard input.

		filemanager.find('input').on('input', function(e){

			folders = [];
			files = [];

			var value = this.value.trim();

			if(value.length > 3) {

				filemanager.addClass('searching');

				// Update the hash on every key stroke
				window.location.hash = 'search=' + value.trim();

			}

			else {

				filemanager.removeClass('searching');
				window.location.hash = encodeURIComponent(currentPath);

			}

		}).on('keyup', function(e){

			// Clicking 'ESC' button triggers focusout and cancels the search

			var search = $(this);

			if(e.keyCode == 27) {

				search.trigger('focusout');

			}

		}).focusout(function(e){

			// Cancel the search

			var search = $(this);

			if(!search.val().trim().length) {

				window.location.hash = encodeURIComponent(currentPath);
				search.hide();
				search.parent().find('span').show();

			}

		});

		// Clicking on folders

		fileList.on('click', 'li.folders', function(e){
			e.preventDefault();

			var nextDir = $(this).find('a.folders').attr('href');

			if(filemanager.hasClass('searching')) {

				// Building the breadcrumbs

				breadcrumbsUrls = generateBreadcrumbs(nextDir);

				filemanager.removeClass('searching');
				filemanager.find('input[type=search]').val('').hide();
				filemanager.find('span').show();
			}
			else {
				breadcrumbsUrls.push(nextDir);
			}

			window.location.hash = encodeURIComponent(nextDir);
			currentPath = nextDir;
		});

		// Clicking on breadcrumbs

		breadcrumbs.on('click', 'a', function(e){
			e.preventDefault();

			var index = breadcrumbs.find('a').index($(this)),
				nextDir = breadcrumbsUrls[index];

			breadcrumbsUrls.length = Number(index);

			window.location.hash = encodeURIComponent(nextDir);

		});

		// Navigates to the given hash (path)

		function goto(hash) {

			hash = decodeURIComponent(hash).slice(1).split('=');

			if (hash.length) {
				var rendered = '';

				// if hash has search in it

				if (hash[0] === 'search') {

					filemanager.addClass('searching');
					rendered = searchData(response, hash[1].toLowerCase());

					if (rendered.length) {
						currentPath = hash[0];
						render(rendered);
					}
					else {
						render(rendered);
					}

				}

				// if hash is some path

				else if (hash[0].trim().length) {

					rendered = searchByPath(hash[0]);

					if (rendered.length) {

						currentPath = hash[0];
						breadcrumbsUrls = generateBreadcrumbs(hash[0]);
						render(rendered);

					}
					else {
						currentPath = hash[0];
						breadcrumbsUrls = generateBreadcrumbs(hash[0]);
						render(rendered);
					}

				}

				// if there is no hash

				else {
					currentPath = data.path;
					breadcrumbsUrls.push(data.path);
					render(searchByPath(data.path));
				}
			}
		}

		// Splits a file path and turns it into clickable breadcrumbs

		function generateBreadcrumbs(nextDir){
			var path = nextDir.split('/').slice(0);
			for(var i=1;i<path.length;i++){
				path[i] = path[i-1]+ '/' +path[i];
			}
			return path;
		}

		// Locates a file by path

		function searchByPath(dir) {
			var path = dir.split('/'),
				demo = response,
				flag = 0;

			for(var i=0;i<path.length;i++){
				for(var j=0;j<demo.length;j++){
					if(demo[j].name === path[i]){
						flag = 1;
						demo = demo[j].items;
						break;
					}
				}
			}

			demo = flag ? demo : [];
			return demo;
		}

		// Recursively search through the file tree

		function searchData(data, searchTerms) {

			data.forEach(function(d){
				if(d.type === 'folder') {

					searchData(d.items,searchTerms);

					if(d.name.toLowerCase().match(searchTerms)) {
						folders.push(d);
					}
				}
				else if(d.type === 'file') {
					if(d.name.toLowerCase().match(searchTerms)) {
						files.push(d);
					}
				}
			});
			return {folders: folders, files: files};
		}

		// Render the HTML for the file manager

		function render(data) {

			var scannedFolders = [],
				scannedFiles = [];

			if(Array.isArray(data)) {

				data.forEach(function (d) {

					if (d.type === 'folder') {
						scannedFolders.push(d);
					}
					else if (d.type === 'file') {
						scannedFiles.push(d);
					}

				});

			}
			else if(typeof data === 'object') {

				scannedFolders = data.folders;
				scannedFiles = data.files;

			}

			// Empty the old result and make the new one

			fileList.empty().hide();

			if(!scannedFolders.length && !scannedFiles.length) {
				filemanager.find('.nothingfound').fadeIn();
			}
			else {
				filemanager.find('.nothingfound').fadeOut();
			}

			if(scannedFolders.length) {

				scannedFolders.forEach(function(f) {

					var itemsLength = f.items.length,
						name = escapeHTML(f.name),
						icon = '';

					if(itemsLength) {
						icon = '';
					}

					if(itemsLength == 1) {
						itemsLength += ' item';
					}
					else if(itemsLength > 1) {
						itemsLength += ' items';
					}
					else {
						itemsLength = 'Empty';
					}

					var folder = $('
'+icon+'' + name + ' ' + itemsLength + '
');
					folder.appendTo(fileList);
				});

			}

			if(scannedFiles.length) {

				scannedFiles.forEach(function(f) {

					var fileSize = bytesToSize(f.size),
						name = escapeHTML(f.name),
						fileType = name.split('.'),
						icon = '';

					fileType = fileType.length > 1 ? fileType[fileType.length-1] : '';

					icon = '' + fileType + '';

					var file = $('
'+icon+''+ name +' '+fileSize+'
');
					file.appendTo(fileList);
				});

			}

			// Generate the breadcrumbs

			var url = '';

			if(filemanager.hasClass('searching')){

				url = 'Search results: ';
				fileList.removeClass('animated');

			}
			else {

				fileList.addClass('animated');

				breadcrumbsUrls.forEach(function (u, i) {

					var name = u.split('/');

					if (i !== breadcrumbsUrls.length - 1) {
						url += '' + name[name.length-1] + ' → ';
					}
					else {
						url += '' + name[name.length-1] + '';
					}

				});

			}

			breadcrumbs.text('').append(url);

			// Show the generated elements

			fileList.animate({'display':'inline-block'});

		}

		// This function escapes special html characters in names

		function escapeHTML(text) {
			return text.replace(/\&/g,'&').replace(/\</g,'<').replace(/\>/g,'>');
		}

		// Convert file sizes from bytes to human readable units

		function bytesToSize(bytes) {
			var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
			if (bytes == 0) return '0 Bytes';
			var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
			return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i];
		}

	});
});
<commit_msg>Initial implementation of sync count<commit_after>$(function(){

	var filemanager = $('.filemanager'),
		breadcrumbs = $('.breadcrumbs'),
		fileList = filemanager.find('.data');

	// Start by fetching the file data from scan route with an AJAX request

	$.get('scan', function(data) {

		var response = [data],
			currentPath = '',
			breadcrumbsUrls = [];

		var folders = [],
			files = [];

		// This event listener monitors changes on the URL. We use it to
		// capture back/forward navigation in the browser.

		$(window).on('hashchange', function(){

			goto(window.location.hash);

			// We are triggering the event. This will execute
			// this function on page load, so that we show the correct folder:

		}).trigger('hashchange');

		// Hiding and showing the search box

		filemanager.find('.search').click(function(){

			var search = $(this);

			search.find('span').hide();
			search.find('input[type=search]').show().focus();

		});

		// Listening for keyboard input on the search field.
		// We are using the "input" event which detects cut and paste
		// in addition to keyboard input.

		filemanager.find('input').on('input', function(e){

			folders = [];
			files = [];

			var value = this.value.trim();

			if(value.length > 3) {

				filemanager.addClass('searching');

				// Update the hash on every key stroke
				window.location.hash = 'search=' + value.trim();

			}

			else {

				filemanager.removeClass('searching');
				window.location.hash = encodeURIComponent(currentPath);

			}

		}).on('keyup', function(e){

			// Clicking 'ESC' button triggers focusout and cancels the search

			var search = $(this);

			if(e.keyCode == 27) {

				search.trigger('focusout');

			}

		}).focusout(function(e){

			// Cancel the search

			var search = $(this);

			if(!search.val().trim().length) {

				window.location.hash = encodeURIComponent(currentPath);
				search.hide();
				search.parent().find('span').show();

			}

		});

		// Clicking on folders

		fileList.on('click', 'li.folders', function(e){
			e.preventDefault();

			var nextDir = $(this).find('a.folders').attr('href');

			if(filemanager.hasClass('searching')) {

				// Building the breadcrumbs

				breadcrumbsUrls = generateBreadcrumbs(nextDir);

				filemanager.removeClass('searching');
				filemanager.find('input[type=search]').val('').hide();
				filemanager.find('span').show();
			}
			else {
				breadcrumbsUrls.push(nextDir);
			}

			window.location.hash = encodeURIComponent(nextDir);
			currentPath = nextDir;
		});

		// Clicking on breadcrumbs

		breadcrumbs.on('click', 'a', function(e){
			e.preventDefault();

			var index = breadcrumbs.find('a').index($(this)),
				nextDir = breadcrumbsUrls[index];

			breadcrumbsUrls.length = Number(index);

			window.location.hash = encodeURIComponent(nextDir);

		});

		// Navigates to the given hash (path)

		function goto(hash) {

			hash = decodeURIComponent(hash).slice(1).split('=');

			if (hash.length) {
				var rendered = '';

				// if hash has search in it

				if (hash[0] === 'search') {

					filemanager.addClass('searching');
					rendered = searchData(response, hash[1].toLowerCase());

					if (rendered.length) {
						currentPath = hash[0];
						render(rendered);
					}
					else {
						render(rendered);
					}

				}

				// if hash is some path

				else if (hash[0].trim().length) {

					rendered = searchByPath(hash[0]);

					if (rendered.length) {

						currentPath = hash[0];
						breadcrumbsUrls = generateBreadcrumbs(hash[0]);
						render(rendered);

					}
					else {
						currentPath = hash[0];
						breadcrumbsUrls = generateBreadcrumbs(hash[0]);
						render(rendered);
					}

				}

				// if there is no hash

				else {
					currentPath = data.path;
					breadcrumbsUrls.push(data.path);
					render(searchByPath(data.path));
				}
			}
		}

		// Splits a file path and turns it into clickable breadcrumbs

		function generateBreadcrumbs(nextDir){
			var path = nextDir.split('/').slice(0);
			for(var i=1;i<path.length;i++){
				path[i] = path[i-1]+ '/' +path[i];
			}
			return path;
		}

		// Locates a file by path

		function searchByPath(dir) {
			var path = dir.split('/'),
				demo = response,
				flag = 0;

			for(var i=0;i<path.length;i++){
				for(var j=0;j<demo.length;j++){
					if(demo[j].name === path[i]){
						flag = 1;
						demo = demo[j].items;
						break;
					}
				}
			}

			demo = flag ? demo : [];
			return demo;
		}

		// Recursively search through the file tree

		function searchData(data, searchTerms) {

			data.forEach(function(d){
				if(d.type === 'folder') {

					searchData(d.items,searchTerms);

					if(d.name.toLowerCase().match(searchTerms)) {
						folders.push(d);
					}
				}
				else if(d.type === 'file') {
					if(d.name.toLowerCase().match(searchTerms)) {
						files.push(d);
					}
				}
			});
			return {folders: folders, files: files};
		}

		// Render the HTML for the file manager

		function render(data) {

			var scannedFolders = [],
				scannedFiles = [],
				syncingFiles = 0;

			if(Array.isArray(data)) {

				data.forEach(function (d) {

					if (d.type === 'folder') {
						scannedFolders.push(d);
					}
					else if (d.type === 'file') {
						if (d.name.indexOf('.!sync') > 0)
							syncingFiles++;
						else
							scannedFiles.push(d);
					}

				});

			}
			else if(typeof data === 'object') {

				scannedFolders = data.folders;
				scannedFiles = data.files;
				syncingFiles = data.syncing;
			}

			// Empty the old result and make the new one

			fileList.empty().hide();

			if(!scannedFolders.length && !scannedFiles.length) {
				filemanager.find('.nothingfound').fadeIn();
			}
			else {
				filemanager.find('.nothingfound').fadeOut();
			}

			if(scannedFolders.length) {

				scannedFolders.forEach(function(f) {

					var itemsLength = f.items.length,
						name = escapeHTML(f.name),
						icon = '';

					if(itemsLength) {
						icon = '';
					}

					if(itemsLength == 1) {
						itemsLength += ' item';
					}
					else if(itemsLength > 1) {
						itemsLength += ' items';
					}
					else {
						itemsLength = 'Empty';
					}

					var folder = $('
'+icon+'' + name + ' ' + itemsLength + '
');
					folder.appendTo(fileList);
				});

			}

			if(scannedFiles.length) {

				scannedFiles.forEach(function(f) {

					var fileSize = bytesToSize(f.size),
						name = escapeHTML(f.name),
						fileType = name.split('.'),
						icon = '';

					fileType = fileType.length > 1 ? fileType[fileType.length-1] : '';

					icon = '' + fileType + '';

					var file = $('
'+icon+''+ name +' '+fileSize+'
');
					file.appendTo(fileList);
				});

			}

			// Generate the breadcrumbs

			var url = '';

			if(filemanager.hasClass('searching')){

				url = 'Search results: ';
				fileList.removeClass('animated');

			}
			else {

				fileList.addClass('animated');

				breadcrumbsUrls.forEach(function (u, i) {

					var name = u.split('/');

					if (i !== breadcrumbsUrls.length - 1) {
						url += '' + name[name.length-1] + ' → ';
					}
					else {
						url += '' + name[name.length-1];

						if ( syncingFiles > 0 )
							url += ' (syncing ' + (syncingFiles > 1 ? 'files ': 'file') + ')'

						url += '';
					}

				});

			}

			breadcrumbs.text('').append(url);

			// Show the generated elements

			fileList.animate({'display':'inline-block'});

		}

		// This function escapes special html characters in names

		function escapeHTML(text) {
			return text.replace(/\&/g,'&').replace(/\</g,'<').replace(/\>/g,'>');
		}

		// Convert file sizes from bytes to human readable units

		function bytesToSize(bytes) {
			var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
			if (bytes == 0) return '0 Bytes';
			var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
			return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i];
		}

	});
});
<|endoftext|>