<commit_before>var Class = require('std/Class'),
	Publisher = require('std/Publisher'),
	extend = require('std/extend')

module.exports = Class(Publisher, function(supr) {

	this._elTag = 'div'
	this._elType = null

	this.getElement = function() {
		if (this._el) { return this._el }
		this._el = this._dom({ tag:this._elTag, type:this._elType })
		if (this._createContent) { this._createContent() }
		return this._el
	}

	/* Dom nodes
	 ***********/
	var domDefaults = {
		tag: 'div'
	}
	this._dom = function(opts) {
		opts = extend(opts, domDefaults)
		var el = document.createElement(opts.tag)
		if (opts.type) { el.type = opts.type }
		if (opts.html) { el.innerHTML = opts.html }
		if (opts['class']) { el.className = opts['class'] }
		if (opts.style) { this.style(el, opts.style) }
		if (opts.value) { el.value = opts.value }
		return el
	}

	this.html = function(el, html) {
		if (typeof html != 'string') {
			html = el
			el = this.getElement()
		}
		el.innerHTML = html
	}

	this.appendTo = function(el) {
		if (typeof el.getElement == 'function') { el = el.getElement() }
		el.appendChild(this.getElement())
		return this
	}

	this.append = function(el) {
		if (typeof el.getElement == 'function') { el = el.getElement() }
		this.getElement().appendChild(el)
		return el
	}

	/* Styles
	 ********/
	this.style = function(el, styleProps) {
		if (!styleProps) {
			styleProps = el
			el = this.getElement()
		}
		var elStyle = el.style
		for (var key in styleProps) {
			var value = styleProps[key]
			if (typeof value == 'number') { value += 'px' }
			elStyle[key] = value
		}
		return this
	}

	this.remove = function(el) {
		el = el || this._el
		if (!el || !el.parentNode) { return this }
		el.parentNode.removeChild(el)
		return this
	}

	/* Class names
	 *************/
	this.addClass = function(el, className) {
		if (typeof el == 'string') {
			className = el
			el = this.getElement()
		}
		if (!(' ' + el.className + ' ').match(' ' + className + ' ')) {
			el.className += ' ' + className + ' '
		}
		return this
	}

	this.removeClass = function(el, className) {
		if (typeof el == 'string') {
			className = el
			el = this.getElement()
		}
		if (el.className) {
			className += ' '
			var current = el.className
			var index = current.indexOf(className)
			if (index != -1) {
				el.className = current.slice(0, index) + current.slice(index + className.length)
			}
		}
		return this
	}

	this.toggleClass = function(el, className, shouldHave) {
		if (arguments.length == 2) {
			shouldHave = className
			className = el
			el = this._el
		}
		if (shouldHave) { this.addClassName(el, className) }
		else { this.removeClassName(el, className) }
		return this
	}

	this._hasClass = function(el, className) {
		if (arguments.length == 1) {
			className = el
			el = this._el
		}
		return !!this._el.className.match(' ' + className + ' ')
	}

	/* Events
	 ********/
	// this._on('click', function(e){})
	// this._on(el, 'click', function(e){})
	this._on = function(el, eventName, handler) {
		if (arguments.length == 2) {
			handler = eventName
			eventName = el
			el = this._el
		}

		// Is removeEvent going to work properly when we wrap the handler in another function?
		function normalizeEvent(e) {
			e = e || event
			var eventObj = { keyCode: e.keyCode, metaKey: e.metaKey, target: e.target || e.srcElement }
			eventObj.cancel = function() {
				if (e.preventDefault) { e.preventDefault() }
				else { e.returnValue = false }
			}
			if (e.type == 'keypress') {
				eventObj.charCode = (eventObj.charCode == 13 && eventObj.keyCode == 13)
				? 0 // in Webkit, return gives a charCode as well as a keyCode. Should only be a keyCode
				: e.charCode
			}
			handler(eventObj)
		}

		if (el.addEventListener) {
			el.addEventListener(eventName, normalizeEvent, false)
		} else if (el.attachEvent){
			el.attachEvent("on"+eventName, normalizeEvent)
		}
		return this
	}

	this._delegateOn = function(el, eventName, handler) {
		if (arguments.length == 2) {
			handler = eventName
			eventName = el
			el = this._el
		}
		this._on(el, eventName, bind(this, '_onDelegatedEvent', handler))
	}

	this._onDelegatedEvent = function(handler, e) {
		var target = e.target
		while(target) {
			if (!target.delegateID) {
				target = target.parentNode
				continue
			}
			handler(target.delegateID, target)
			return
		}
	}

	/* Positioning
	 *************/
	// dom offset code adopted from jQuery JavaScript Library v1.3.2
	/*!
	 * jQuery JavaScript Library v1.3.2
	 * http://jquery.com/
	 *
	 * Copyright (c) 2009 John Resig
	 * Dual licensed under the MIT and GPL licenses.
	 * http://docs.jquery.com/License
	 *
	 * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
	 * Revision: 6246
	 */
	this.layout = function(el) {
		if (!el) { el = this._el }
		var win = window;

		if (el.getBoundingClientRect) {
			var box = el.getBoundingClientRect(), doc = el.ownerDocument, body = doc.body, docElem = doc.documentElement,
				clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
				top  = box.top  + (win.pageYOffset || docElem.scrollTop  || body.scrollTop ) - clientTop,
				left = box.left + (win.pageXOffset || docElem.scrollLeft || body.scrollLeft) - clientLeft;
			return { y: top, x: left, w: box.right - box.left, h: box.bottom - box.top };

		} else {
			var offset = arguments.callee.offset;
			if (!offset) {
				var body = document.body, container = document.createElement('div'), innerDiv, checkDiv, table, td, rules, prop, bodyMarginTop = body.style.marginTop,
					html = '

';

				rules = { position: 'absolute', top: 0, left: 0, margin: 0, border: 0, width: '1px', height: '1px', visibility: 'hidden' };
				for (prop in rules) container.style[prop] = rules[prop];

				container.innerHTML = html;
				body.insertBefore(container, body.firstChild);
				innerDiv = container.firstChild, checkDiv = innerDiv.firstChild, td = innerDiv.nextSibling.firstChild.firstChild;

				var offset = {};
				offset.doesNotAddBorder = (checkDiv.offsetTop !== 5);
				offset.doesAddBorderForTableAndCells = (td.offsetTop === 5);

				innerDiv.style.overflow = 'hidden', innerDiv.style.position = 'relative';
				offset.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);

				body.style.marginTop = '1px';
				offset.doesNotIncludeMarginInBodyOffset = (body.offsetTop === 0);
				body.style.marginTop = bodyMarginTop;

				body.removeChild(container);
				arguments.callee.offset = offset;
			}

			var height = el.offsetHeight;
			var width = el.offsetWidth;

			var offsetParent = el.offsetParent, prevOffsetParent = el,
				doc = el.ownerDocument, computedStyle, docElem = doc.documentElement,
				body = doc.body, defaultView = doc.defaultView,
				prevComputedStyle = defaultView.getComputedStyle(el, null),
				top = el.offsetTop, left = el.offsetLeft;

			while ((el = el.parentNode) && el !== body && el !== docElem) {
				computedStyle = defaultView.getComputedStyle(el, null);
				top -= el.scrollTop, left -= el.scrollLeft;
				if (el === offsetParent) {
					top += el.offsetTop, left += el.offsetLeft;
					if (offset.doesNotAddBorder && !(offset.doesAddBorderForTableAndCells && /^t(able|d|h)$/i.test(el.tagName))) {
						top += parseInt(computedStyle.borderTopWidth, 10) || 0;
						left += parseInt(computedStyle.borderLeftWidth, 10) || 0;
					}
					prevOffsetParent = offsetParent;
					offsetParent = el.offsetParent;
				}
				if (offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible") {
					top += parseInt(computedStyle.borderTopWidth, 10) || 0;
					left += parseInt(computedStyle.borderLeftWidth, 10) || 0;
				}
				prevComputedStyle = computedStyle;
			}

			if (prevComputedStyle.position === "relative" || prevComputedStyle.position === "static") {
				top += body.offsetTop;
				left += body.offsetLeft;
			}

			if (prevComputedStyle.position === "fixed") {
				top  += Math.max(docElem.scrollTop, body.scrollTop);
				left += Math.max(docElem.scrollLeft, body.scrollLeft);
			}

			return { y: top, x: left, w: width, h: height };
		}
	}
	// end jQuery positioning code
})
<commit_msg>Rename _dom to dom<commit_after>var Class = require('std/Class'),
	Publisher = require('std/Publisher'),
	extend = require('std/extend')

module.exports = Class(Publisher, function(supr) {

	this._elTag = 'div'
	this._elType = null

	this.getElement = function() {
		if (this._el) { return this._el }
		this._el = this.dom({ tag:this._elTag, type:this._elType })
		if (this._createContent) { this._createContent() }
		return this._el
	}

	/* Dom nodes
	 ***********/
	var domDefaults = {
		tag: 'div'
	}
	this.dom = function(opts) {
		opts = extend(opts, domDefaults)
		var el = document.createElement(opts.tag)
		if (opts.type) { el.type = opts.type }
		if (opts.html) { el.innerHTML = opts.html }
		if (opts['class']) { el.className = opts['class'] }
		if (opts.style) { this.style(el, opts.style) }
		if (opts.value) { el.value = opts.value }
		return el
	}
	this._dom = this.dom // deprecated

	this.html = function(el, html) {
		if (typeof html != 'string') {
			html = el
			el = this.getElement()
		}
		el.innerHTML = html
	}

	this.appendTo = function(el) {
		if (typeof el.getElement == 'function') { el = el.getElement() }
		el.appendChild(this.getElement())
		return this
	}

	this.append = function(el) {
		if (typeof el.getElement == 'function') { el = el.getElement() }
		this.getElement().appendChild(el)
		return el
	}

	/* Styles
	 ********/
	this.style = function(el, styleProps) {
		if (!styleProps) {
			styleProps = el
			el = this.getElement()
		}
		var elStyle = el.style
		for (var key in styleProps) {
			var value = styleProps[key]
			if (typeof value == 'number') { value += 'px' }
			elStyle[key] = value
		}
		return this
	}

	this.remove = function(el) {
		el = el || this._el
		if (!el || !el.parentNode) { return this }
		el.parentNode.removeChild(el)
		return this
	}

	/* Class names
	 *************/
	this.addClass = function(el, className) {
		if (typeof el == 'string') {
			className = el
			el = this.getElement()
		}
		if (!(' ' + el.className + ' ').match(' ' + className + ' ')) {
			el.className += ' ' + className + ' '
		}
		return this
	}

	this.removeClass = function(el, className) {
		if (typeof el == 'string') {
			className = el
			el = this.getElement()
		}
		if (el.className) {
			className += ' '
			var current = el.className
			var index = current.indexOf(className)
			if (index != -1) {
				el.className = current.slice(0, index) + current.slice(index + className.length)
			}
		}
		return this
	}

	this.toggleClass = function(el, className, shouldHave) {
		if (arguments.length == 2) {
			shouldHave = className
			className = el
			el = this._el
		}
		if (shouldHave) { this.addClassName(el, className) }
		else { this.removeClassName(el, className) }
		return this
	}

	this._hasClass = function(el, className) {
		if (arguments.length == 1) {
			className = el
			el = this._el
		}
		return !!this._el.className.match(' ' + className + ' ')
	}

	/* Events
	 ********/
	// this._on('click', function(e){})
	// this._on(el, 'click', function(e){})
	this._on = function(el, eventName, handler) {
		if (arguments.length == 2) {
			handler = eventName
			eventName = el
			el = this._el
		}

		// Is removeEvent going to work properly when we wrap the handler in another function?
		function normalizeEvent(e) {
			e = e || event
			var eventObj = { keyCode: e.keyCode, metaKey: e.metaKey, target: e.target || e.srcElement }
			eventObj.cancel = function() {
				if (e.preventDefault) { e.preventDefault() }
				else { e.returnValue = false }
			}
			if (e.type == 'keypress') {
				eventObj.charCode = (eventObj.charCode == 13 && eventObj.keyCode == 13)
				? 0 // in Webkit, return gives a charCode as well as a keyCode. Should only be a keyCode
				: e.charCode
			}
			handler(eventObj)
		}

		if (el.addEventListener) {
			el.addEventListener(eventName, normalizeEvent, false)
		} else if (el.attachEvent){
			el.attachEvent("on"+eventName, normalizeEvent)
		}
		return this
	}

	this._delegateOn = function(el, eventName, handler) {
		if (arguments.length == 2) {
			handler = eventName
			eventName = el
			el = this._el
		}
		this._on(el, eventName, bind(this, '_onDelegatedEvent', handler))
	}

	this._onDelegatedEvent = function(handler, e) {
		var target = e.target
		while(target) {
			if (!target.delegateID) {
				target = target.parentNode
				continue
			}
			handler(target.delegateID, target)
			return
		}
	}

	/* Positioning
	 *************/
	// dom offset code adopted from jQuery JavaScript Library v1.3.2
	/*!
	 * jQuery JavaScript Library v1.3.2
	 * http://jquery.com/
	 *
	 * Copyright (c) 2009 John Resig
	 * Dual licensed under the MIT and GPL licenses.
	 * http://docs.jquery.com/License
	 *
	 * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
	 * Revision: 6246
	 */
	this.layout = function(el) {
		if (!el) { el = this._el }
		var win = window;

		if (el.getBoundingClientRect) {
			var box = el.getBoundingClientRect(), doc = el.ownerDocument, body = doc.body, docElem = doc.documentElement,
				clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
				top  = box.top  + (win.pageYOffset || docElem.scrollTop  || body.scrollTop ) - clientTop,
				left = box.left + (win.pageXOffset || docElem.scrollLeft || body.scrollLeft) - clientLeft;
			return { y: top, x: left, w: box.right - box.left, h: box.bottom - box.top };

		} else {
			var offset = arguments.callee.offset;
			if (!offset) {
				var body = document.body, container = document.createElement('div'), innerDiv, checkDiv, table, td, rules, prop, bodyMarginTop = body.style.marginTop,
					html = '

';

				rules = { position: 'absolute', top: 0, left: 0, margin: 0, border: 0, width: '1px', height: '1px', visibility: 'hidden' };
				for (prop in rules) container.style[prop] = rules[prop];

				container.innerHTML = html;
				body.insertBefore(container, body.firstChild);
				innerDiv = container.firstChild, checkDiv = innerDiv.firstChild, td = innerDiv.nextSibling.firstChild.firstChild;

				var offset = {};
				offset.doesNotAddBorder = (checkDiv.offsetTop !== 5);
				offset.doesAddBorderForTableAndCells = (td.offsetTop === 5);

				innerDiv.style.overflow = 'hidden', innerDiv.style.position = 'relative';
				offset.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);

				body.style.marginTop = '1px';
				offset.doesNotIncludeMarginInBodyOffset = (body.offsetTop === 0);
				body.style.marginTop = bodyMarginTop;

				body.removeChild(container);
				arguments.callee.offset = offset;
			}

			var height = el.offsetHeight;
			var width = el.offsetWidth;

			var offsetParent = el.offsetParent, prevOffsetParent = el,
				doc = el.ownerDocument, computedStyle, docElem = doc.documentElement,
				body = doc.body, defaultView = doc.defaultView,
				prevComputedStyle = defaultView.getComputedStyle(el, null),
				top = el.offsetTop, left = el.offsetLeft;

			while ((el = el.parentNode) && el !== body && el !== docElem) {
				computedStyle = defaultView.getComputedStyle(el, null);
				top -= el.scrollTop, left -= el.scrollLeft;
				if (el === offsetParent) {
					top += el.offsetTop, left += el.offsetLeft;
					if (offset.doesNotAddBorder && !(offset.doesAddBorderForTableAndCells && /^t(able|d|h)$/i.test(el.tagName))) {
						top += parseInt(computedStyle.borderTopWidth, 10) || 0;
						left += parseInt(computedStyle.borderLeftWidth, 10) || 0;
					}
					prevOffsetParent = offsetParent;
					offsetParent = el.offsetParent;
				}
				if (offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible") {
					top += parseInt(computedStyle.borderTopWidth, 10) || 0;
					left += parseInt(computedStyle.borderLeftWidth, 10) || 0;
				}
				prevComputedStyle = computedStyle;
			}

			if (prevComputedStyle.position === "relative" || prevComputedStyle.position === "static") {
				top += body.offsetTop;
				left += body.offsetLeft;
			}

			if (prevComputedStyle.position === "fixed") {
				top  += Math.max(docElem.scrollTop, body.scrollTop);
				left += Math.max(docElem.scrollLeft, body.scrollLeft);
			}

			return { y: top, x: left, w: width, h: height };
		}
	}
	// end jQuery positioning code
})
<|endoftext|>


<commit_before>'use strict';

function ProxyServerAddon(project) {
  this.project = project;
  this.name = 'proxy-server-middleware';
}

ProxyServerAddon.prototype.serverMiddleware = function(options) {
  var app = options.app, server = options.options.httpServer;
  options = options.options;

  if (options.proxy) {
    var proxy = require('http-proxy').createProxyServer({
      target: options.proxy,
      ws: true,
      changeOrigin: true
    });

    var morgan  = require('morgan');

    options.ui.writeLine('Proxying to ' + options.proxy);

    server.on('upgrade', function (req, socket, head) {
      options.ui.writeLine('Proxying websocket to ' + req.url);
      proxy.ws(req, socket, head);
    });

    app.use(morgan('dev'));
    app.use(function(req, res) {
      return proxy.web(req, res);
    });
  }
};

module.exports = ProxyServerAddon;
<commit_msg>Close #2691 ember server --proxy crashes and exits when Internet connection is lost<commit_after>'use strict';

function ProxyServerAddon(project) {
  this.project = project;
  this.name = 'proxy-server-middleware';
}

ProxyServerAddon.prototype.serverMiddleware = function(options) {
  var app = options.app, server = options.options.httpServer;
  options = options.options;

  if (options.proxy) {
    var proxy = require('http-proxy').createProxyServer({
      target: options.proxy,
      ws: true,
      changeOrigin: true
    });

    proxy.on('error', function (e) {
      options.ui.writeLine('Error proxying to ' + options.proxy);
      options.ui.writeError(e);
    });

    var morgan  = require('morgan');

    options.ui.writeLine('Proxying to ' + options.proxy);

    server.on('upgrade', function (req, socket, head) {
      options.ui.writeLine('Proxying websocket to ' + req.url);
      proxy.ws(req, socket, head);
    });

    app.use(morgan('dev'));
    app.use(function(req, res) {
      return proxy.web(req, res);
    });
  }
};

module.exports = ProxyServerAddon;
<|endoftext|>


<commit_before>TextButton = function (game, text, x, y, func, ctx) {
    Phaser.Group.call(this, game);

    var sound = game.add.audio('button')

    var btn = this.game.add.button(x,
				   y,
				   'sprites',
				   func,
				   ctx,
				   'button',
				   'button',
				   'button-pressed')
    btn.anchor.setTo(0.5)
    btn.onInputDown.add(function(){sound.play()}, this);

    this.button = btn
    this.add(btn)

    var style = { font: "31px Verdana",
		  fontWeight: 'bold',
		  stroke: '#000000',
		  strokeThickness: 3,
		  fill: "#EEEEEE",
		  align: "center"};
    var text = this.game.add.text(x, y, text, style)
    text.anchor.setTo(0.5)
    this.text = text
    this.add(text);
}

TextButton.prototype = Object.create(Phaser.Group.prototype)
TextButton.prototype.constructor = TextButton

TextButton.prototype.enable = function(enable = true) {
    if(!enable){
	this.button.tint = 0x999999
	this.button.inputEnabled = false
    }
    else {
	this.button.tint = 0xFFFFFF
	this.button.inputEnabled = true
    }
}
<commit_msg>fix half a sound-bug<commit_after>TextButton = function (game, text, x, y, func, ctx) {
    Phaser.Group.call(this, game);

    var sound = game.add.audio('button')

    var btn = this.game.add.button(x,
				   y,
				   'sprites',
				   func,
				   ctx,
				   'button',
				   'button',
				   'button-pressed')
    btn.anchor.setTo(0.5)
    btn.onInputDown.add(function(){
	if(!game.masterMute)
	    sound.play()
    }, this);

    this.button = btn
    this.add(btn)

    var style = { font: "31px Verdana",
		  fontWeight: 'bold',
		  stroke: '#000000',
		  strokeThickness: 3,
		  fill: "#EEEEEE",
		  align: "center"};
    var text = this.game.add.text(x, y, text, style)
    text.anchor.setTo(0.5)
    this.text = text
    this.add(text);
}

TextButton.prototype = Object.create(Phaser.Group.prototype)
TextButton.prototype.constructor = TextButton

TextButton.prototype.enable = function(enable = true) {
    if(!enable){
	this.button.tint = 0x999999
	this.button.inputEnabled = false
    }
    else {
	this.button.tint = 0xFFFFFF
	this.button.inputEnabled = true
    }
}
<|endoftext|>


<commit_before>'use strict'

function Thermostat(){
  this.temperature = 20;
};

Thermostat.prototype.getCurrentTemperature = function(){
  return this.temperature;
};

Thermostat.prototype.up = function() {
  this.temperature += 1;
}
<commit_msg>added down function<commit_after>'use strict'

function Thermostat(){
  this.temperature = 20;
};

Thermostat.prototype.getCurrentTemperature = function(){
  return this.temperature;
};

Thermostat.prototype.up = function() {
  this.temperature += 1;
};

Thermostat.prototype.down = function() {
  this.temperature -= 1;
}
<|endoftext|>


<commit_before>define([
  'd3',
  'underscore',
  'components/component'
], function(d3, _, Component) {

  //Tool does everything a component does, but has different defaults
  //And possibly some extra methods
  var Tool = Component.extend({
    init: function(core, options) {
      // Define default template
      this.template = this.template || "tools/tool";
      this.profiles = this.profiles || {
        'default': {
          timeslider: true,
          buttonlist: true,
          title: true
        }
      };
      this.layout = core.getInstance("layout");

      // Same constructor as widgets
      this._super(core, options);
    },

    //Tools renders just like the widgets, but they update the layout
    render: function() {
      var _this = this;
      return this._super(function() {
        _this.layout.setContainer(_this.element);
        _this.layout.setProfile(_this.profiles);
        _this.layout.update();
        //binds resize event to update
        _this.events.bind('resize', function() {
          _this.layout.update();
        });

        _this.events.bind('change:state', function(state) {
        });

      });
    }

  });

  return Tool;
});
<commit_msg>Removed call to this.layout as that is preloaded through Components<commit_after>define([
  'd3',
  'underscore',
  'components/component'
], function(d3, _, Component) {

  //Tool does everything a component does, but has different defaults
  //And possibly some extra methods
  var Tool = Component.extend({
    init: function(core, options) {
      // Define default template
      this.template = this.template || "tools/tool";
      this.profiles = this.profiles || {
        'default': {
          timeslider: true,
          buttonlist: true,
          title: true
        }
      };

      // Same constructor as widgets
      this._super(core, options);
    },

    //Tools renders just like the widgets, but they update the layout
    render: function() {
      var _this = this;
      return this._super(function() {
        _this.layout.setContainer(_this.element);
        _this.layout.setProfile(_this.profiles);
        _this.layout.update();
        //binds resize event to update
        _this.events.bind('resize', function() {
          _this.layout.update();
        });

        _this.events.bind('change:state', function(state) {
        });

      });
    }

  });

  return Tool;
});
<|endoftext|>


<commit_before>import tween from './utils/tween';
import Orb from './orb';
import viewer from './viewer';
import props from './props';
import * as SDFText from './sdftext';
import * as THREE from './lib/three';
import { offsetFrom } from './utils/three';
import settings from './settings';

// Scene storage
const transitionScene = new THREE.Scene();
let mainScene;

// Set up stage
const textCreator = SDFText.creator();
const text = textCreator.create('', {
  wrapWidth: 4000,
  scale: 15,
  align: 'center',
  color: settings.textColor,
});

transitionScene.add(text);
transitionScene.add(props.grid);

const floatingOrb = new Orb(transitionScene);

let time = 0;
let fadedOut = false;
let insideTransition = false;

const tick = (dt) => {
  time += dt;
  floatingOrb.mesh.position.y = Math.sin(time * 2) / 4 + 2;
};

const tweenFog = (from, to, duration = 2) => {
  viewer.renderScene.fog.far = from;
  return new Promise(resolve => {
    tween(
      viewer.renderScene.fog,
      {
        far: to,
        ease: 'easeOutCubic',
        duration,
      }
    ).on('complete', resolve);
  });
};

const fadeOut = async (duration) => {
  fadedOut = true;
  await tweenFog(25, 0, duration);
};

const fadeIn = async (maxFogDistance, duration) => {
  fadedOut = false;
  await tweenFog(0, maxFogDistance, duration);
};

export default {
  fadeOut,

  async enter(param) {
    // If fadeOut wasn't called before enter:
    if (!fadedOut) {
      await fadeOut();
    }
    insideTransition = true;
    mainScene = viewer.scene;
    viewer.renderScene = transitionScene;
    transitionScene.fog = new THREE.Fog(0x000000, 0, 0);

    floatingOrb.fadeIn();
    viewer.events.on('tick', tick);
    text.updateLabel(param.text);
    text.position.copy(offsetFrom(viewer.camera, -5, 2, -10));
    text.lookAt(viewer.camera.position);
    floatingOrb.mesh.position.copy(offsetFrom(viewer.camera, 2, 0, -8));
    floatingOrb.mesh.scale.set(4, 4, 4);
    // Fade in the transition space:
    await fadeIn(25);
  },

  async exit({ immediate } = {}) {
    if (!insideTransition) return;
    if (!fadedOut) {
      await fadeOut();
    }
    viewer.events.off('tick', tick);

    // Switch scenes:
    viewer.renderScene = mainScene;

    insideTransition = false;
    await fadeIn(300, immediate ? 0 : undefined);
  },
};
<commit_msg>fix transition immediate option<commit_after>import tween from './utils/tween';
import Orb from './orb';
import viewer from './viewer';
import props from './props';
import * as SDFText from './sdftext';
import * as THREE from './lib/three';
import { offsetFrom } from './utils/three';
import settings from './settings';

// Scene storage
const transitionScene = new THREE.Scene();
let mainScene;

// Set up stage
const textCreator = SDFText.creator();
const text = textCreator.create('', {
  wrapWidth: 4000,
  scale: 15,
  align: 'center',
  color: settings.textColor,
});

transitionScene.add(text);
transitionScene.add(props.grid);

const floatingOrb = new Orb(transitionScene);

let time = 0;
let fadedOut = false;
let insideTransition = false;

const tick = (dt) => {
  time += dt;
  floatingOrb.mesh.position.y = Math.sin(time * 2) / 4 + 2;
};

const tweenFog = (from, to, duration = 2) => {
  viewer.renderScene.fog.far = from;
  return new Promise(resolve => {
    tween(
      viewer.renderScene.fog,
      {
        far: to,
        ease: 'easeOutCubic',
        duration,
      }
    ).on('complete', resolve);
  });
};

const fadeOut = async (duration) => {
  fadedOut = true;
  await tweenFog(25, 0, duration);
};

const fadeIn = async (maxFogDistance, duration) => {
  fadedOut = false;
  await tweenFog(0, maxFogDistance, duration);
};

export default {
  fadeOut,

  async enter(param) {
    // If fadeOut wasn't called before enter:
    if (!fadedOut) {
      await fadeOut();
    }
    insideTransition = true;
    mainScene = viewer.scene;
    viewer.renderScene = transitionScene;
    transitionScene.fog = new THREE.Fog(0x000000, 0, 0);

    floatingOrb.fadeIn();
    viewer.events.on('tick', tick);
    text.updateLabel(param.text);
    text.position.copy(offsetFrom(viewer.camera, -5, 2, -10));
    text.lookAt(viewer.camera.position);
    floatingOrb.mesh.position.copy(offsetFrom(viewer.camera, 2, 0, -8));
    floatingOrb.mesh.scale.set(4, 4, 4);
    // Fade in the transition space:
    await fadeIn(25);
  },

  async exit({ immediate } = {}) {
    if (!insideTransition) return;
    if (!fadedOut) {
      await fadeOut();
    }
    viewer.events.off('tick', tick);

    // Switch scenes:
    viewer.renderScene = mainScene;

    insideTransition = false;
    const far = 300;
    if (immediate) {
      viewer.renderScene.fog.far = far;
    } else {
      await fadeIn(far);
    }
  },
};
<|endoftext|>


<commit_before>import { matchRoutes } from 'little-router'
import { changeRoute, renderRoute } from './actions'
import { isFunc, isPromise, sanitize } from './utils'
import { STATUS_OK, STATUS_NOT_FOUND } from './const'

export default async (options = {}) => {
  const {
    strict,
    routes = [],
    location = {},
    dispatch,
    beforeRender,
  } = options

  const route = {}
  const params = {}
  const actions = []
  const resolvers = {}
  const components = {}

  let promises
  let sanitized

  // match route branches
  const branches = matchRoutes({
    path: location.pathname || '',
    routes,
    strict,
  })

  // reduce route object while extracting
  // actions, resolvers, and components
  branches.forEach((branch, index) => {
    const { route: { action, resolve, component, ...rest } } = branch

    Object.assign(route, rest)
    Object.assign(params, branch.params)

    if (action) {
      actions.push(action)
    }
    if (resolve) {
      resolvers[index] = resolve
    }
    if (component) {
      components[index] = component
    }
  })

  // toggle loading flag
  route.loading = true

  // ensure route has a status code
  if (!route.status) {
    route.status = branches.length ? STATUS_OK : STATUS_NOT_FOUND
  }

  // these are serializable but we don't want
  // them added to the state or passed down
  delete route.routes

  // prepare route object for dispatch
  sanitized = sanitize(route)

  // create container for middleware updates
  const transition = {}

  // dispatch route change action
  if (dispatch) {
    dispatch(changeRoute({
      route: sanitized,
      params,
      location,
      transition,
    }))
  }

  // resolve routes with async properties
  promises = []
  Object.keys(resolvers).forEach(index => {
    const resolver = resolvers[index]
    const response = Promise.resolve(resolver({ route, params, location }))
    promises.push(response.then(({
      action,
      component,
      ...props
    } = {}) => {
      Object.assign(route, props)
      if (action) {
        actions.push(action)
      }
      if (component) {
        components[index] = component
      }
    }))
  })

  if (promises.length) {
    await Promise.all(promises)
  }

  // dispatch route actions
  if (dispatch) {
    promises = []
    actions.forEach(action => {
      if (isFunc(action)) {
        const promise = dispatch(action({ route, params, location }))
        if (isPromise(promise)) {
          promises.push(promise)
        }
      } else {
        dispatch(action)
      }
    })
    if (promises.length) {
      await Promise.all(promises)
    }
  }

  // exit if transition was invalidated
  // by a route action
  if (transition.invalid) {
    return undefined
  }

  // toggle loading flag
  route.loading = false

  // sort components into proper order before
  // adding back to route
  route.components = Object.keys(components).sort().map(key => (
    components[key]
  ))

  // define a result object since
  // we need it twice below
  const result = {
    route,
    routes,
    strict,
    params,
    location,
  }

  // this callback is useful for ensuring
  // custom actions get dispatched in the
  // same render cycle that gets triggered by
  // the actions being triggered in this file
  if (isFunc(beforeRender)) {
    beforeRender(result)
  }

  // exit if transition was invalidated
  // by beforeRender callback
  if (transition.invalid) {
    return undefined
  }

  // update local route status code if a status change
  // was dispatched in route action or beforeRender
  if (transition.status) {
    route.status = transition.status
  }

  // prepare route object for dispatch
  sanitized = sanitize(route)

  // we don't want these in the state
  delete sanitized.components

  // dispatch render route action
  if (dispatch) {
    dispatch(renderRoute({
      route: sanitized,
      params,
      location,
    }))
  }

  return result
}
<commit_msg>Handle promises returned by transition.beforeRender<commit_after>import { matchRoutes } from 'little-router'
import { changeRoute, renderRoute } from './actions'
import { isFunc, isPromise, sanitize } from './utils'
import { STATUS_OK, STATUS_NOT_FOUND } from './const'

export default async (options = {}) => {
  const {
    strict,
    routes = [],
    location = {},
    dispatch,
    beforeRender,
  } = options

  const route = {}
  const params = {}
  const actions = []
  const resolvers = {}
  const components = {}

  let promise
  let promises
  let sanitized

  // match route branches
  const branches = matchRoutes({
    path: location.pathname || '',
    routes,
    strict,
  })

  // reduce route object while extracting
  // actions, resolvers, and components
  branches.forEach((branch, index) => {
    const { route: { action, resolve, component, ...rest } } = branch

    Object.assign(route, rest)
    Object.assign(params, branch.params)

    if (action) {
      actions.push(action)
    }
    if (resolve) {
      resolvers[index] = resolve
    }
    if (component) {
      components[index] = component
    }
  })

  // toggle loading flag
  route.loading = true

  // ensure route has a status code
  if (!route.status) {
    route.status = branches.length ? STATUS_OK : STATUS_NOT_FOUND
  }

  // these are serializable but we don't want
  // them added to the state or passed down
  delete route.routes

  // prepare route object for dispatch
  sanitized = sanitize(route)

  // create container for middleware updates
  const transition = {}

  // dispatch route change action
  if (dispatch) {
    dispatch(changeRoute({
      route: sanitized,
      params,
      location,
      transition,
    }))
  }

  // resolve routes with async properties
  promises = []
  Object.keys(resolvers).forEach(index => {
    const resolver = resolvers[index]
    const response = Promise.resolve(resolver({ route, params, location }))
    promises.push(response.then(({
      action,
      component,
      ...props
    } = {}) => {
      Object.assign(route, props)
      if (action) {
        actions.push(action)
      }
      if (component) {
        components[index] = component
      }
    }))
  })

  if (promises.length) {
    await Promise.all(promises)
  }

  // dispatch route actions
  if (dispatch) {
    promises = []
    actions.forEach(action => {
      if (isFunc(action)) {
        promise = dispatch(action({ route, params, location }))
        if (isPromise(promise)) {
          promises.push(promise)
        }
      } else {
        dispatch(action)
      }
    })
    if (promises.length) {
      await Promise.all(promises)
    }
  }

  // exit if transition was invalidated
  // by a route action
  if (transition.invalid) {
    return undefined
  }

  // toggle loading flag
  route.loading = false

  // sort components into proper order before
  // adding back to route
  route.components = Object.keys(components).sort().map(key => (
    components[key]
  ))

  // define a result object since
  // we need it twice below
  const result = {
    route,
    routes,
    strict,
    params,
    location,
  }

  // this callback is useful for ensuring
  // custom actions get dispatched in the
  // same render cycle that gets triggered by
  // the actions being triggered in this file
  if (isFunc(beforeRender)) {
    promise = beforeRender(result)
    if (isPromise(promise)) {
      await promise
    }
  }

  // exit if transition was invalidated
  // by beforeRender callback
  if (transition.invalid) {
    return undefined
  }

  // update local route status code if a status change
  // was dispatched in route action or beforeRender
  if (transition.status) {
    route.status = transition.status
  }

  // prepare route object for dispatch
  sanitized = sanitize(route)

  // we don't want these in the state
  delete sanitized.components

  // dispatch render route action
  if (dispatch) {
    dispatch(renderRoute({
      route: sanitized,
      params,
      location,
    }))
  }

  return result
}
<|endoftext|>


<commit_before>'use strict';

UheerApp
    .factory('MusicPlayer', ['Synchronizer', 'MusicStreamProvider', 'PlaysetIterator',
    function (Synchronizer, MusicStreamProvider, PlaysetIterator) {
        return {
            /// Take a $scope.
            /// Considerations: The MusicStreamProvider will be initialized and previous instances will be disposed, removing existing streamings.
            take: function ($scope) {
                this._muted = false;

                this.$scope = $scope;

                PlaysetIterator.take($scope.channel);
                MusicStreamProvider.initialize();

                return this;
            },

            /// Mutes the current music acording with the value of shouldBeMuted.
            mute: function (shouldBeMuted) {
                this._muted = shouldBeMuted;

                if (this.$scope.channel.Current) {
                    var audio = MusicStreamProvider.audioById(this.$scope.channel.Current.Id);
                    if (audio) audio.muted = shouldBeMuted;
                }

                return this;
            },

            /// Start the player.
            /// Considerations:
            ///     The client will re-sync with the server.
            ///     The music with Id=this.channel.CurrentId will be played.
            start: function () {
                if (!this.$scope.channel.CurrentId) {
                    console.log('Cannot play a channel that is currently stalled.');
                    return this;
                }

                console.log('Let\'s hear the playset in channel ' + this.$scope.channel.Name + '!');

                this.$scope.loading = true;

                var _this = this;
                var initialMusic = this.$scope.channel.Current;
                if (!initialMusic) {
                    console.log('The music {' + this.$scope.channel.currentId + '} could not be found. There is a issue with this channel.');
                }

                this.$scope.loading = false;

                Synchronizer
                    .take(this.$scope)
                    .onSynchronized(function (timeFrame) {
                        _this.play(timeFrame);
                    })
                    .sync();

                return this;
            },

            /// Play a music with Id=musicId from the this.channel.Musics list.
            /// Considerations:
            ///     If musicId were not provided, it assumes this.channel.CurrentId as replacement.
            ///     Defines a callback to play the next music (constrained by some Channel definitions).
            play: function (startAt) {
                var music = this.$scope.channel.Current;
                if (!music) {
                    console.log('Channel #' + this.$scope.channel.Id + ' doesn\'t have any music on play.');
                    return;
                }

                console.log('Channel #' + this.$scope.channel.Id + ' will play "' + music.Name + '", starting at ' + (startAt || 0) + '.');
                this.$scope.isPlaying = true;

                this.$scope.currentMusic = music;

                // Two musics should never play at once.
                this.stopAll();

                // Start streaming it, if it hasn't been done yet.
                var audio = this.audioOnPlay = MusicStreamProvider.stream(music.Id);

                var play_handler;
                audio.addEventListener('canplay', play_handler = function () {
                    audio.currentTime = startAt.toFixed(4);
                    audio.muted = this._muted;
                    audio.play();
                    this.playing = true;

                    audio.removeEventListener('canplay', play_handler)
                });

                var _this = this;

                //if (this._continuousSyncHandler)
                //    clearInterval(this._continuousSyncHandler);
                //this._continuousSyncHandler = setInterval(function () {
                //    _this.continuousSync();
                //}, 5 * 1000);

                // Modifies progress-bar as music progresses.
                audio.ontimeupdate = null;

                var update_handler;
                audio.addEventListener('timeupdate', update_handler = function () {
                    if (_this.audioOnPlay) {
                        _this.$scope.$apply(function () {
                            _this.$scope.currentMusicCurrentTime = Math.trunc(_this.audioOnPlay.currentTime);
                        });
                    }
                }, false);

                var remove_handler;
                audio.addEventListener('ended', remove_handler = function () {
                    _this.$scope.isPlaying = false;
                    _this.audioOnPlay = null;

                    audio.removeEventListener('timeupdate', update_handler);
                    audio.removeEventListener('ended', remove_handler);

                    Synchronizer.translatePlayset();
                }, false);

                return this.streamFollowingMusics(music, 2);
            },

            /// Stop all playing audios.
            stopAll: function () {
                for (var index in this.$scope.channel.Musics) {
                    var music = this.$scope.channel.Musics[index];
                    this.stop(music);
                }

                this.playing = false;
                return this;
            },

            /// Stop the audio that makes reference to the music with Id=music.Id.
            stop: function (music) {
                var channel = this.$scope.channel;

                music = music || channel.Current;
                if (music) {
                    var audio = MusicStreamProvider.audioById(music.Id);
                    if (audio) {
                        audio.pause();
                        audio.oncanplay = audio.ontimeupdate = audio.onended = null;
                    }

                    if (music == channel.Current)
                        this.playing = false;
                }

                return this;
            },

            continuousSync: function () {
                console.log('Synchronization attempt...');
                console.log(this.audioOnPlay);

                if (this.audioOnPlay) {
                    //
                }
            },

            streamFollowingMusics: function (music, count) {
                var next = music;
                for (var i = 0; i < count && next; i++) {
                    var next = PlaysetIterator.peak(1);
                    if (next) MusicStreamProvider.stream(next.Id);
                }

                return this;
            }
        };
    }]);

UheerApp
    .factory('Synchronizer', ['StatusResource', 'PlaysetIterator',
    function (Status, PlaysetIterator) {
        return {
            onSynchronized: function (callback) {
                this._callback = callback;
                return this;
            },

            take: function ($scope) {
                this.$scope = $scope;
                return this;
            },

            sync: function () {
                console.log('Synchronization procedure method has started.');
                this.$scope.synchronized = false;

                var channel = this.$scope.channel;

                // Let's find the channel's length. This information will help us optimizing the
                // sync operation performance when the channel has been on for many hours.
                this._channelsLength = 0;
                for (var i in channel.Musics) this._channelsLength += channel.Musics[i].LengthInMilliseconds;

                var _this = this;
                var timeframe = new Date();

                Status.now().$promise.then(function (response) {
                    var localTime = new Date();

                    // Cristian's algorithm.
                    timeframe = localTime - timeframe;
                    var delay = timeframe / 2;

                    var remoteTime = new Date(Date.parse(response.Now));
                    remoteTime.setMilliseconds(remoteTime.getMilliseconds() + delay);

                    console.log('The synchronization time-frame was ' + timeframe + '.');
                    _this.differenceBetweenRemoteAndLocal = remoteTime - localTime;
                    _this.translatePlayset();
                });

                return this;
            },

            /// Translates all the time-frame gotten from the server and moves
            /// the stack to the music that is currently being played.
            translatePlayset: function () {
                console.log('Playset translation procedure has started.');

                var channel = this.$scope.channel;

                var currentStartTime = channel.CurrentStartTime;
                var remoteTime = new Date();
                remoteTime.setMilliseconds(remoteTime.getMilliseconds() + this.differenceBetweenRemoteAndLocal);

                var timeline = remoteTime - channel.CurrentStartTime;

                if (timeline > this._channelsLength && !channel.Loops) {
                    PlaysetIterator.kill();
                    return
                }

                // The channel may have looped already and the cycle would put us in the exact same spot.
                // We don't need to iterate throughout the entire list to check this. Instead, let's just consider the last one.
                if (this._channelsLength) timeline %= this._channelsLength;

                while (timeline > channel.Current.LengthInMilliseconds) {
                    timeline -= channel.Current.LengthInMilliseconds;

                    PlaysetIterator.next();
                    if (!channel.Current) {
                        PlaysetIterator.kill();
                        return
                    }
                }

                console.log('Translation procedure found ' + channel.Current.Name + '(' + channel.Current.Id + ') as current music.');

                this.$scope.synchronized = true;

                // If there is a callback, invoke it passing the timeline in seconds, which represents the current position of the song's playment.
                if (this._callback) return this._callback(timeline / 1000);
            }
        };
    }]);

UheerApp
    .factory('MusicStreamProvider', [
        '$document', 'config',
        function ($document, config) {
            return {
                initialize: function () {
                    return this.dispose();
                },

                dispose: function () {
                    // Stop streaming and pause all audios. Finally, remove their references for good.
                    if (this._streams) {
                        var musicIds = Object.keys(this._streams);
                        for (var i in musicIds) {
                            this.remove(musicIds[i]);
                        }
                    }

                    this._streams = {
                    };
                    return this;
                },

                audioById: function (musicId) {
                    return this._streams[musicId.toString()];
                },

                exists: function (musicId) {
                    return this.audioById(musicId) != null;
                },

                /// Creates or retrieves a audio element that streams the music with Id=musicId.
                stream: function (musicId) {
                    var musicAlreadyStreaming = this._streams[musicId.toString()];
                    if (musicAlreadyStreaming) return musicAlreadyStreaming;

                    console.log('Starting to stream music #' + musicId + '.');

                    var audio = $document[0].createElement('audio');
                    audio.src = config.apiUrl + 'Musics/' + musicId + '/Stream';
                    audio.preload = 'auto';

                    return this._streams[musicId.toString()] = audio;
                },

                /// Remove a music from the streaming list.
                remove: function (musicId) {
                    var audio = this._streams[musicId.toString()];
                    if (!audio) return false;

                    audio.pause();
                    audio.preload = 'none';
                    audio.src = '';

                    delete this._streams[musicId.toString()];
                    return true;
                }
            };
        }]);

UheerApp
    .factory('PlaysetIterator', function () {
        return {
            take: function (channel) {
                this._channel = channel;

                // No music is currently being played.
                if (!this._channel.CurrentId) {
                    this._channel.Current = null;
                    this._channel.CurrentIndex = -1;
                    return this;
                }

                // Find the current music.
                this._channel.CurrentIndex = this.indexOf(this._channel.CurrentId);
                this._channel.Current = this._channel.Musics[this._channel.CurrentIndex];

                return this;
            },

            /// Retrieves the index of the music with Id=musicId. If none, returns -1.
            /// Please avoid using this method, considering it iterates through the entire list.
            indexOf: function (musicId) {
                var musics = this._channel.Musics;

                for (var i = 0; i < musics.length; i++)
                    if (musics[i].Id == musicId)
                        return i;

                return -1;
            },

            /// Set the next music as the current of the playset.
            /// Consider the next music as the one that follows the current in the channel.Musics list,
            /// or the first one, case the current is also the last in the list.
            next: function () {
                // There isn't a next if there is not a current.
                if (this._channel.CurrentIndex == -1) {
                    return this;
                }

                if (this._channel.CurrentIndex == this._channel.Musics.length - 1 && !this._channel.Loops) {
                    console.log('Iterator has reached end of ' + this._channel.Name + ' \'s playlist.');
                    this._channel.Current = null;
                    return this;
                }

                var nextIndex = (this._channel.CurrentIndex + 1) % this._channel.Musics.length;
                var nextMusic = this._channel.Musics[nextIndex];

                this._channel.CurrentStartTime.setMilliseconds(
                    this._channel.CurrentStartTime.getMilliseconds() + this._channel.Current.LengthInMilliseconds)

                this._channel.CurrentIndex = nextIndex;
                this._channel.Current = nextMusic;

                return this;
            },

            /// Peak the next music of the channel.
            /// Take the music that's currently being played as starting point.
            /// @param count: the count(th) music, from the current, to be peaked. If no argument was passed, assume 0.
            peak: function (count) {
                // There isn't a next if there is not a current or if we've reached the end of the list.
                if (this._channel.CurrentIndex == -1
                    || this._channel.CurrentIndex == this._channel.Musics.length - 1 && !this._channel.Loops) {
                    return null;
                }

                return this._channel.Musics[(this._channel.CurrentIndex + (count || 0)) % this._channel.Musics.length];
            },

            /// Set current music to null, forcing the channel to stop.
            kill: function () {
                this._current = null;
                this._currentIndex = -1;
            }
        }
    });

UheerApp
    .run(['$rootScope', 'MusicStreamProvider', 'MusicPlayer', function ($rootScope, MusicStreamProvider, MusicPlayer) {
        $rootScope.$on("$stateChangeStart", function (event, toState, toParams, fromState, fromParams) {
            // Asserts behavior: player will stop if user move to another page.
            if (fromState.name == 'listen/:id') {
                MusicPlayer.stopAll();
                MusicStreamProvider.dispose();
            }
        });
    }]);
<commit_msg>Firefox finally passes musics.<commit_after>'use strict';

UheerApp
    .factory('MusicPlayer', ['Synchronizer', 'MusicStreamProvider', 'PlaysetIterator',
    function (Synchronizer, MusicStreamProvider, PlaysetIterator) {
        return {
            /// Take a $scope.
            /// Considerations: The MusicStreamProvider will be initialized and previous instances will be disposed, removing existing streamings.
            take: function ($scope) {
                this._muted = false;

                this.$scope = $scope;

                PlaysetIterator.take($scope.channel);
                MusicStreamProvider.initialize();

                return this;
            },

            /// Mutes the current music acording with the value of shouldBeMuted.
            mute: function (shouldBeMuted) {
                this._muted = shouldBeMuted;

                if (this.$scope.channel.Current) {
                    var audio = MusicStreamProvider.audioById(this.$scope.channel.Current.Id);
                    if (audio) audio.muted = shouldBeMuted;
                }

                return this;
            },

            /// Start the player.
            /// Considerations:
            ///     The client will re-sync with the server.
            ///     The music with Id=this.channel.CurrentId will be played.
            start: function () {
                if (!this.$scope.channel.CurrentId) {
                    console.log('Cannot play a channel that is currently stalled.');
                    return this;
                }

                console.log('Let\'s hear the playset in channel ' + this.$scope.channel.Name + '!');

                this.$scope.loading = true;

                var _this = this;
                var initialMusic = this.$scope.channel.Current;
                if (!initialMusic) {
                    console.log('The music {' + this.$scope.channel.currentId + '} could not be found. There is a issue with this channel.');
                }

                this.$scope.loading = false;

                Synchronizer
                    .take(this.$scope)
                    .onSynchronized(function (timeFrame) {
                        _this.play(timeFrame);
                    })
                    .sync();

                return this;
            },

            /// Play a music with Id=musicId from the this.channel.Musics list.
            /// Considerations:
            ///     If musicId were not provided, it assumes this.channel.CurrentId as replacement.
            ///     Defines a callback to play the next music (constrained by some Channel definitions).
            play: function (startAt) {
                var music = this.$scope.channel.Current;
                if (!music) {
                    console.log('Channel #' + this.$scope.channel.Id + ' doesn\'t have any music on play.');
                    return;
                }

                console.log('Channel #' + this.$scope.channel.Id + ' will play "' + music.Name + '", starting at ' + (startAt || 0) + '.');
                this.$scope.isPlaying = true;

                this.$scope.currentMusic = music;

                // Two musics should never play at once.
                this.stopAll();

                // Start streaming it, if it hasn't been done yet.
                var audio = this.audioOnPlay = MusicStreamProvider.stream(music.Id);

                try {
                    console.log('Trying to start music synchronously...');
                    audio.currentTime = (startAt).toFixed(4);
                    audio.muted = this._muted;
                    audio.play();
                    this.playing = true;
                    console.log('Done!');
                } catch (e) {
                    console.log('Failed. Buffer is required. Waiting...');

                    var play_handler;
                    var preparationFrame = new Date();
                    audio.addEventListener('canplay', play_handler = function () {
                        console.log('Done!');
                        preparationFrame = (new Date() - preparationFrame) / 1000;

                        console.log(music.Name + ' can play!');
                        audio.currentTime = (startAt + preparationFrame).toFixed(4);
                        audio.muted = this._muted;
                        audio.play();
                        this.playing = true;

                        audio.removeEventListener('canplay', play_handler)
                    });
                }

                var _this = this;

                //if (this._continuousSyncHandler)
                //    clearInterval(this._continuousSyncHandler);
                //this._continuousSyncHandler = setInterval(function () {
                //    _this.continuousSync();
                //}, 5 * 1000);

                // Modifies progress-bar as music progresses.
                audio.ontimeupdate = null;

                var update_handler;
                audio.addEventListener('timeupdate', update_handler = function () {
                    if (_this.audioOnPlay) {
                        _this.$scope.$apply(function () {
                            _this.$scope.currentMusicCurrentTime = Math.trunc(_this.audioOnPlay.currentTime);
                        });
                    }
                }, false);

                var remove_handler;
                audio.addEventListener('ended', remove_handler = function () {
                    console.log(music.Name + ' has ended.');
                    _this.$scope.isPlaying = false;
                    _this.audioOnPlay = null;

                    audio.removeEventListener('timeupdate', update_handler);
                    audio.removeEventListener('ended', remove_handler);

                    Synchronizer.translatePlayset();
                }, false);

                return this.streamFollowingMusics(music, 2);
            },

            /// Stop all playing audios.
            stopAll: function () {
                for (var index in this.$scope.channel.Musics) {
                    var music = this.$scope.channel.Musics[index];
                    this.stop(music);
                }

                this.playing = false;
                return this;
            },

            /// Stop the audio that makes reference to the music with Id=music.Id.
            stop: function (music) {
                var channel = this.$scope.channel;

                music = music || channel.Current;
                if (music) {
                    var audio = MusicStreamProvider.audioById(music.Id);
                    if (audio) {
                        audio.pause();
                        audio.oncanplay = audio.ontimeupdate = audio.onended = null;
                    }

                    if (music == channel.Current)
                        this.playing = false;
                }

                return this;
            },

            continuousSync: function () {
                console.log('Synchronization attempt...');
                console.log(this.audioOnPlay);

                if (this.audioOnPlay) {
                    //
                }
            },

            streamFollowingMusics: function (music, count) {
                var next = music;
                for (var i = 0; i < count && next; i++) {
                    var next = PlaysetIterator.peak(1);
                    if (next) MusicStreamProvider.stream(next.Id);
                }

                return this;
            }
        };
    }]);

UheerApp
    .factory('Synchronizer', ['StatusResource', 'PlaysetIterator',
    function (Status, PlaysetIterator) {
        return {
            onSynchronized: function (callback) {
                this._callback = callback;
                return this;
            },

            take: function ($scope) {
                this.$scope = $scope;
                return this;
            },

            sync: function () {
                console.log('Synchronization procedure method has started.');
                this.$scope.synchronized = false;

                var channel = this.$scope.channel;

                // Let's find the channel's length. This information will help us optimizing the
                // sync operation performance when the channel has been on for many hours.
                this._channelsLength = 0;
                for (var i in channel.Musics) this._channelsLength += channel.Musics[i].LengthInMilliseconds;

                var _this = this;
                var timeframe = new Date();

                Status.now().$promise.then(function (response) {
                    var localTime = new Date();

                    // Cristian's algorithm.
                    timeframe = localTime - timeframe;
                    var delay = timeframe / 2;

                    var remoteTime = new Date(Date.parse(response.Now));
                    remoteTime.setMilliseconds(remoteTime.getMilliseconds() + delay);

                    console.log('The synchronization time-frame was ' + timeframe + '.');
                    _this.differenceBetweenRemoteAndLocal = remoteTime - localTime;
                    _this.translatePlayset();
                });

                return this;
            },

            /// Translates all the time-frame gotten from the server and moves
            /// the stack to the music that is currently being played.
            translatePlayset: function () {
                console.log('Playset translation procedure has started.');

                var channel = this.$scope.channel;

                var currentStartTime = channel.CurrentStartTime;
                var remoteTime = new Date();
                remoteTime.setMilliseconds(remoteTime.getMilliseconds() + this.differenceBetweenRemoteAndLocal);

                var timeline = remoteTime - channel.CurrentStartTime;

                if (timeline > this._channelsLength && !channel.Loops) {
                    PlaysetIterator.kill();
                    return
                }

                // The channel may have looped already and the cycle would put us in the exact same spot.
                // We don't need to iterate throughout the entire list to check this. Instead, let's just consider the last one.
                if (this._channelsLength) timeline %= this._channelsLength;

                while (timeline > channel.Current.LengthInMilliseconds) {
                    timeline -= channel.Current.LengthInMilliseconds;

                    PlaysetIterator.next();
                    if (!channel.Current) {
                        PlaysetIterator.kill();
                        return
                    }
                }

                console.log('Translation procedure found ' + channel.Current.Name + '(' + channel.Current.Id + ') as current music.');

                this.$scope.synchronized = true;

                // If there is a callback, invoke it passing the timeline in seconds, which represents the current position of the song's playment.
                if (this._callback) return this._callback(timeline / 1000);
            }
        };
    }]);

UheerApp
    .factory('MusicStreamProvider', [
        '$document', 'config',
        function ($document, config) {
            return {
                initialize: function () {
                    return this.dispose();
                },

                dispose: function () {
                    // Stop streaming and pause all audios. Finally, remove their references for good.
                    if (this._streams) {
                        var musicIds = Object.keys(this._streams);
                        for (var i in musicIds) {
                            this.remove(musicIds[i]);
                        }
                    }

                    this._streams = {
                    };
                    return this;
                },

                audioById: function (musicId) {
                    return this._streams[musicId.toString()];
                },

                exists: function (musicId) {
                    return this.audioById(musicId) != null;
                },

                /// Creates or retrieves a audio element that streams the music with Id=musicId.
                stream: function (musicId) {
                    var musicAlreadyStreaming = this._streams[musicId.toString()];
                    if (musicAlreadyStreaming) return musicAlreadyStreaming;

                    console.log('Starting to stream music #' + musicId + '.');

                    var audio = $document[0].createElement('audio');
                    audio.src = config.apiUrl + 'Musics/' + musicId + '/Stream';
                    audio.preload = 'auto';

                    return this._streams[musicId.toString()] = audio;
                },

                /// Remove a music from the streaming list.
                remove: function (musicId) {
                    var audio = this._streams[musicId.toString()];
                    if (!audio) return false;

                    audio.pause();
                    audio.preload = 'none';
                    audio.src = '';

                    delete this._streams[musicId.toString()];
                    return true;
                }
            };
        }]);

UheerApp
    .factory('PlaysetIterator', function () {
        return {
            take: function (channel) {
                this._channel = channel;

                // No music is currently being played.
                if (!this._channel.CurrentId) {
                    this._channel.Current = null;
                    this._channel.CurrentIndex = -1;
                    return this;
                }

                // Find the current music.
                this._channel.CurrentIndex = this.indexOf(this._channel.CurrentId);
                this._channel.Current = this._channel.Musics[this._channel.CurrentIndex];

                return this;
            },

            /// Retrieves the index of the music with Id=musicId. If none, returns -1.
            /// Please avoid using this method, considering it iterates through the entire list.
            indexOf: function (musicId) {
                var musics = this._channel.Musics;

                for (var i = 0; i < musics.length; i++)
                    if (musics[i].Id == musicId)
                        return i;

                return -1;
            },

            /// Set the next music as the current of the playset.
            /// Consider the next music as the one that follows the current in the channel.Musics list,
            /// or the first one, case the current is also the last in the list.
            next: function () {
                // There isn't a next if there is not a current.
                if (this._channel.CurrentIndex == -1) {
                    return this;
                }

                if (this._channel.CurrentIndex == this._channel.Musics.length - 1 && !this._channel.Loops) {
                    console.log('Iterator has reached end of ' + this._channel.Name + ' \'s playlist.');
                    this._channel.Current = null;
                    return this;
                }

                var nextIndex = (this._channel.CurrentIndex + 1) % this._channel.Musics.length;
                var nextMusic = this._channel.Musics[nextIndex];

                this._channel.CurrentStartTime.setMilliseconds(
                    this._channel.CurrentStartTime.getMilliseconds() + this._channel.Current.LengthInMilliseconds)

                this._channel.CurrentIndex = nextIndex;
                this._channel.Current = nextMusic;

                return this;
            },

            /// Peak the next music of the channel.
            /// Take the music that's currently being played as starting point.
            /// @param count: the count(th) music, from the current, to be peaked. If no argument was passed, assume 0.
            peak: function (count) {
                // There isn't a next if there is not a current or if we've reached the end of the list.
                if (this._channel.CurrentIndex == -1
                    || this._channel.CurrentIndex == this._channel.Musics.length - 1 && !this._channel.Loops) {
                    return null;
                }

                return this._channel.Musics[(this._channel.CurrentIndex + (count || 0)) % this._channel.Musics.length];
            },

            /// Set current music to null, forcing the channel to stop.
            kill: function () {
                this._current = null;
                this._currentIndex = -1;
            }
        }
    });

UheerApp
    .run(['$rootScope', 'MusicStreamProvider', 'MusicPlayer', function ($rootScope, MusicStreamProvider, MusicPlayer) {
        $rootScope.$on("$stateChangeStart", function (event, toState, toParams, fromState, fromParams) {
            // Asserts behavior: player will stop if user move to another page.
            if (fromState.name == 'listen/:id') {
                MusicPlayer.stopAll();
                MusicStreamProvider.dispose();
            }
        });
    }]);
<|endoftext|>


<commit_before>define(
   [
    'config',
    'model',
    'mustache',
    './URLView',
    'text!views/ImportDirectory/main.html'
    ],
    function(config, model, Mustache, URLView, template){

        return Backbone.View.extend({
            initialize : function(){
                db = config.db
                window.wgxpath.install()

                // Set up browser changes listener to watch for and handle Node changes
                //  listener's response
                var changes = db.changes();
                this.template = template;
                // Register necessary subviews
                this.URLView = URLView;
            },
            render: function(){
                // TODO: Consider using assign() as described here:
                //  http://ianstormtaylor.com/rendering-views-in-backbonejs-isnt-always-simple/
                //config.render_to_id(this, "#import_directory_template")
                // Render Mustache template
                $('#content').html(Mustache.render(this.template));
                // Render first subview
                this.URLView = new URLView({el: '#steps'})
                this.URLView.render()
                this.delegateEvents()
            }
        })

});
<commit_msg>Added comment<commit_after>define(
   [
    'config',
    'model',
    'mustache',
    './URLView',
    'text!views/ImportDirectory/main.html'
    ],
    function(config, model, Mustache, URLView, template){

        return Backbone.View.extend({
            initialize : function(){
                db = config.db
                window.wgxpath.install()

                // Set up browser changes listener to watch for and handle Node changes
                //  listener's response
                var changes = db.changes();
                this.template = template;
                // Register necessary subviews
                this.URLView = URLView;
                // TODO: Consider using kimonolabs.com as a web scraper, though maybe not if it costs too much.
            },
            render: function(){
                // TODO: Consider using assign() as described here:
                //  http://ianstormtaylor.com/rendering-views-in-backbonejs-isnt-always-simple/
                //config.render_to_id(this, "#import_directory_template")
                // Render Mustache template
                $('#content').html(Mustache.render(this.template));
                // Render first subview
                this.URLView = new URLView({el: '#steps'})
                this.URLView.render()
                this.delegateEvents()
            }
        })

});
<|endoftext|>


<commit_before>newBooking.controller('NewBookingController', ['$scope', '$http', '$resource', NewBookingController]);

function NewBookingController($scope, $http, $resource) {
    CreateServices($resource);

    $scope.locations = Locations.query({});
    $scope.currentLocation = $scope.locations[0]

    //$scope.$watch('currentLocation', function () { $('#calendar').fullCalendar('refetchEvents') });
    //$scope.$watch('currentRoom', function () { $('#calendar').fullCalendar('refetchEvents') });
    //InitiateCalendar();

    $scope.GetRoomByName = function (roomName) {
        for (var i = 0; i < $scope.bookingFilter.location.rooms.length; i++) {
            if ($scope.bookingFilter.location.rooms[i].roomName == roomName) {
                return $scope.bookingFilter.location.rooms[i];
            }
        }
    }

    $scope.SearchBookings = function () {
        $scope.roomBookings = Available.query({
            location: $scope.bookingFilter.location.name,
            startDate: FormatDateForURL($scope.bookingFilter.startDate),
            endDate: FormatDateForURL($scope.bookingFilter.startDate),
            smartRoom: $scope.bookingFilter.smartRoom,
            numberOfAttendees: $scope.bookingFilter.numberOfAttendees
        }, function (success) {
            $("#bookingTable").html('');

            for (var i = 0; i < success.length; i++) {
                var eventData = [];
                for (var b = 0; b < success[i].bookings.length; b++) {
                    eventData.push({
                        title: success[i].bookings[b].owner,
                        start: new moment(success[i].bookings[b].startTime),
                        end: new moment(success[i].bookings[b].endTime)
                    });
                }
                var roomDetails = '
' + success[i].roomName + '
';
                //debugger;
                roomDetails = roomDetails + ' ' + success[i].computerCount + '';
                roomDetails = roomDetails + ' ' + success[i].phoneCount + '';

                $('#bookingTable').append('

' + roomDetails + '

');
                var roomName = '[' + success[i].roomName + ']';
                $("#" + success[i].roomName + "_calendar").fullCalendar({
                    weekends: false,
                    header: {
                        left: '',
                        center: '',
                        right: ''
                    },
                    //defaultDate: $scope.bookingFilter.startDate,
                    defaultView: 'agendaDay',
                    selectable: true,
                    selectHelper: true,
                    height: 500,
                    titleFormat: '' + roomName + '',
                    select: function (start, end, allDay) {

                        var newEvent = { start: start, end: end };
                        if (isMeetingOverlapping(newEvent, this.calendar)) {
                            alert('New meeting clashes with existing booking. Please choose a new time!"');
                            return;
                        }

                        var $scope = angular.element($("#controllerDiv")).scope();
                        var room = $scope.GetRoomByName(this.title);

                        $scope.newBooking.RoomName = room.roomName;

                        $scope.$digest();
                        $("#confirmModal").modal('show');
                    },
                    events: eventData
                });

            }

        });
    }

    $scope.AddExternalName = function () {

        var fName = $('#externalFirstNameTextBox').val();
        var lName = $('#externalLastNameTextBox').val();

        if (fName === null || lName === null) {
            //TODO: Validation - Duplicate - Null
        }

        $scope.newBooking.ExternalNames.push({
            fName: fName,
            lName: lName
        });

        $('#externalFirstNameTextBox').val('');
        $('#externalLastNameTextBox').val('');
    }

    $scope.RemoveExternalName = function (fName, lName) {
        for (var i = 0; i < $scope.newBooking.ExternalNames.length; i++) {
            if ($scope.newBooking.ExternalNames[i].fName === fName && $scope.newBooking.ExternalNames[i].lName === lName) {
                $scope.newBooking.ExternalNames.splice(i, 1);
                break;
            }
        }
    }

    $scope.NewBooking = function () {

        //Validate Emails
        var emails = $scope.newBooking.AttendeeEmails.split(' ');
        var AttendeeEmails = ValidateEmails(emails);

        //Validate Number Of Emails Matches Attendees
        //Validate Number of External Names is not graether than attendees
        ValidateNoAttendees(emails.length, $scope.bookingFilter.numberOfAttendees, $scope.newBooking.ExternalNames.length);

        //Validate Subject
        var Subject = ValidateSubject($scope.newBooking.Subject);

        Booking.save({
            room: $scope.newBooking.RoomName,
            startDate: $scope.newBooking.StartDate,
            endDate: $scope.newBooking.EndDate,
            subject: Subject,
            attendeeEmails: Emails,
            externalNames: JSON.stringify($scope.newBooking.ExternalNames),
            pc: $scope.newBooking.Pc,
            flipchart: $scope.newBooking.FlipChart,
            projector: $scope.newBooking.Projector
        },
        function (success) {
            alert('Booking Saved');
        },
        function (error) {
            alert('Error');
        })

    }

    $scope.bookingFilter = {
        startDate: new moment().utc().format('DD-MM-YYYY'),
        startTime: '',
        endTime: '',
        location: $scope.currentLocation,
        smartRoom: false,
        numberOfAttendees: 1
    };

    $scope.booking = {
        StartDate: new Date(),
        EndDate: new Date(),
        Room: { RoomName: '' },
        Location: { LocationName: '' }
    };

    $scope.newBooking = {
        RoomName: '',
        Subject: '',
        AttendeeEmails: '',
        ExternalNames: [],
        StartDate: new moment().format("DD-MM-YYYY HH:mm A"), //Change To Chart Input
        EndDate: new moment().format("DD-MM-YYYY HH:mm A"), //Change To Chart Input
        Pc: false,
        FlipChart: false,
        Projector: false
    };
}

function CreateServices($resource) {
    Locations = $resource('/api/locations', {
    }, {
        query: { method: 'GET', isArray: true }
    });

    Available = $resource('/api/availability/:location/:startDate/:endDate/:numberOfAttendees/:smartRoom', { location: 'location', startDate: 'startDate', endDate: 'endDate', numberOfAttendees: 'numberOfAttendees', smartRoom: 'smartRoom' },
    {
        query: { method: 'GET', isArray: true }
    });

    Booking = $resource('/api/bookings/:room/:startDate/:endDate/:subject/:attendeeEmails/:externalNames/:pc/:flipchart/:projector', { room: 'room', startDate: 'startDate', endDate: 'endDate', subject: 'subject', attendeeEmails: 'attendeeEmails', externalNames: 'externalNames', pc: 'pc', flipchart: 'flipchart', projector: 'projector' },
    {
        save: { method: 'POST' }
    });
}

//Caladner UI Functions

function isMeetingOverlapping(event, calendar) {
    var array = calendar.clientEvents();
    for (i in array) {
        if (event.end > array[i].start && event.start < array[i].end) {
            return true;
        }
    }
    return false;
}

function InitiateCalendar() {
    $(function () {
        $('#calendar').fullCalendar({
            weekends: false,
            header: {
                left: 'prev,next today',
                center: 'title',
                right: 'month,agendaWeek,agendaDay'
            },
            events: function (start, end, timezone, callback) {
                var $scope = angular.element($("#controllerDiv")).scope();
                $.ajax({
                    url: '/api/bookings/' + moment.utc(start, "DD-MM-YYYY").format("MM-DD-YYYY") + '/' + moment.utc(end, "DD-MM-YYYY").format("MM-DD-YYYY") + '/' + $scope.currentRoom.roomName,
                    dataType: 'json',
                    type: 'POST',
                    contentType: 'application/json; charset=utf-8',
                    data: JSON.stringify($scope.currentLocation),
                    success: function (data) {
                        var events = [];
                        for (var i = 0; i < data.length; i++) {
                            events.push({
                                title: data[i].owner,
                                start: data[i].startDate,
                                end: data[i].endDate
                            });
                        }
                        callback(events);
                    }
                });

            }
        });
    });
}
///////////////////////////////////////////////////////////////////

//Format Functions

function convertTo24Hour(time) {
    var hours = parseInt(time.substr(0, 2));
    time = time.toString().trim();
    if (time.indexOf('AM') != -1 && hours == 12) {
        time = time.replace('12', '0');
    }
    if (time.indexOf('PM') != -1 && hours < 12) {
        time = time.replace(hours, (hours + 12));
    }
    return time.replace(/(AM|PM)/, '').slice(0, -1);
}

function FormatDateForURL(date) {

    if (date === "") {
        alert('Please Enter a Valid Date');
        throw new Error();
    }

    var parts = date.split('-');
    myDate2 = new Date(parts[2], parts[1] - 1, parts[0]);

    return moment(myDate2).format('MM-DD-YYYY');
}

function FormatTime(time, date) {
    if (time === "") {
        alert('Invalid Time');
        throw new Error();
    }

    var parts = date.split('-');
    var timeSplit = convertTo24Hour(time).split(':');

    if (timeSplit[0] > 17 || (timeSplit[0] == 17 && timeSplit[1] > 0)) {
        alert('Time Must Be Before 5.00 PM');
        throw new Error();
    }
    else if (timeSplit[0] < 9) {
        alert('Time Must Be After 9.00 AM');
        throw new Error();
    };

    var timeDate = new Date(parts[2], parts[1] - 1, parts[0]);
    timeDate.setHours(timeSplit[0], timeSplit[1], 0);

    return timeDate;
}
///////////////////////////////////////////////////////////////////

//New Booking Functions

function ValidateEmails(emails) {

    var attendeeEmails = null;

    if (emails.length < 1) {
        alert('No Emails Detected');
        throw new Error();
    }

    for (var i = 0; i < emails.length; i++) {
        //Validate Each Email
        if (emails[i].trim() === "" || !ValidateEmail(emails[i])) {
            alert('Invalid Email Detected: ' + emails[i]);
            throw new Error();
        }
        else {
            attendeeEmails += emails[i].trim() + ";"
        }
    }

    return attendeeEmails;
}

function ValidateNoAttendees(attendees, filterAttendees, externalNamesLength) {
    if (attendees !== filterAttendees) {
        alert('Number of Atteneds Does Not Match Number of Emails.');
        throw new Error();
    }
    else if (externalNamesLength > attendees) {
        alert('Too Many External names');
        throw new Error();
    }
}

function ValidateSubject(subject) {
    if (subject.trim() === "") {
        SetModalErrorMessage('Invalid Subject Detected');
        throw new Error();
    }
    return subject;
}

function ValidateEmail(email) {
    var regex = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i;
    return regex.test(email);
}

function SetModalErrorMessage(message) {
    if (message === "") {
        $('#newBookingErrorMessage').hide();
    }
    else {
        $('#newBookingErrorMessage').text(message);
        $('#newBookingErrorMessage').show();
    }
}

///////////////////////////////////////////////////////////////////

//Filtering Functions

function ValidateAttendess(attendees) {

    if (attendees === null) {
        throw new Error();
    }
    else if (isNaN(attendees)) {
        alert('Invalid Number');
        throw new Error();
    }
    else if (attendees <= 0) {
        alert('Attendees Can Not Be Negative');
        throw new Error();
    }
    return attendees;
}

function ValidateDates(start, end) {
    //ToDo: Add Equals then
    if (start > end) {
        alert('Start Date Can Not Be Ahead Of End Date');
        throw new Error();
    }
}

function IncrementTime(time, minutes) {
    var timeSplit = time.split(':');
    var amPm = timeSplit[1].split(" ");

    minutes = parseInt(amPm[0]) + minutes

    if (minutes >= 60) {
        minutes -= 60;
        ("0" + minutes).slice(-2);
    }

    return "'" + timeSplit[0] + ":" + minutes + " " + amPm[1] + "'";
}
///////////////////////////////////////////////////////////////////
<commit_msg>Updated calander time range<commit_after>newBooking.controller('NewBookingController', ['$scope', '$http', '$resource', NewBookingController]);

function NewBookingController($scope, $http, $resource) {
    CreateServices($resource);

    $scope.locations = Locations.query({});
    $scope.currentLocation = $scope.locations[0]

    //$scope.$watch('currentLocation', function () { $('#calendar').fullCalendar('refetchEvents') });
    //$scope.$watch('currentRoom', function () { $('#calendar').fullCalendar('refetchEvents') });
    //InitiateCalendar();

    $scope.GetRoomByName = function (roomName) {
        for (var i = 0; i < $scope.bookingFilter.location.rooms.length; i++) {
            if ($scope.bookingFilter.location.rooms[i].roomName == roomName) {
                return $scope.bookingFilter.location.rooms[i];
            }
        }
    }

    $scope.SearchBookings = function () {
        $scope.roomBookings = Available.query({
            location: $scope.bookingFilter.location.name,
            startDate: FormatDateForURL($scope.bookingFilter.startDate),
            endDate: FormatDateForURL($scope.bookingFilter.startDate),
            smartRoom: $scope.bookingFilter.smartRoom,
            numberOfAttendees: $scope.bookingFilter.numberOfAttendees
        }, function (success) {
            $("#bookingTable").html('');

            for (var i = 0; i < success.length; i++) {
                var eventData = [];
                for (var b = 0; b < success[i].bookings.length; b++) {
                    eventData.push({
                        title: success[i].bookings[b].owner,
                        start: new moment(success[i].bookings[b].startDate),
                        end: new moment(success[i].bookings[b].endDate)
                    });
                }
                var roomDetails = '
' + success[i].roomName + '
';
                //debugger;
                roomDetails = roomDetails + ' ' + success[i].computerCount + '';
                roomDetails = roomDetails + ' ' + success[i].phoneCount + '';

                $('#bookingTable').append('

' + roomDetails + '

');
                var roomName = '[' + success[i].roomName + ']';
                $("#" + success[i].roomName + "_calendar").fullCalendar({
                    weekends: false,
                    header: {
                        left: '',
                        center: '',
                        right: ''
                    },
                    //defaultDate: $scope.bookingFilter.startDate,
                    defaultView: 'agendaDay',
                    minTime: "09:00:00",
                    maxTime: "17:00:00",
                    allDaySlot: false,
                    selectable: true,
                    selectHelper: true,
                    height: 500,
                    titleFormat: '' + roomName + '',
                    select: function (start, end, allDay) {

                        var newEvent = { start: start, end: end };
                        if (isMeetingOverlapping(newEvent, this.calendar)) {
                            alert('New meeting clashes with existing booking. Please choose a new time!"');
                            return;
                        }

                        var $scope = angular.element($("#controllerDiv")).scope();
                        var room = $scope.GetRoomByName(this.title);

                        $scope.newBooking.RoomName = room.roomName;
                        $scope.newBooking.StartDate = start.utc().format('DD-MM-YYYY HH:mm A');
                        $scope.newBooking.EndDate = end.utc().format('DD-MM-YYYY HH:mm A');

                        $scope.$digest();
                        $("#confirmModal").modal('show');
                    },
                    events: eventData
                });

            }

        });
    }

    $scope.AddExternalName = function () {

        var fName = $('#externalFirstNameTextBox').val();
        var lName = $('#externalLastNameTextBox').val();

        if (fName === null || lName === null) {
            //TODO: Validation - Duplicate - Null
        }

        $scope.newBooking.ExternalNames.push({
            fName: fName,
            lName: lName
        });

        $('#externalFirstNameTextBox').val('');
        $('#externalLastNameTextBox').val('');
    }

    $scope.RemoveExternalName = function (fName, lName) {
        for (var i = 0; i < $scope.newBooking.ExternalNames.length; i++) {
            if ($scope.newBooking.ExternalNames[i].fName === fName && $scope.newBooking.ExternalNames[i].lName === lName) {
                $scope.newBooking.ExternalNames.splice(i, 1);
                break;
            }
        }
    }

    $scope.NewBooking = function () {

        //Validate Emails
        var emails = $scope.newBooking.AttendeeEmails.split(' ');
        var AttendeeEmails = ValidateEmails(emails);

        //Validate Number Of Emails Matches Attendees
        //Validate Number of External Names is not graether than attendees
        ValidateNoAttendees(emails.length, $scope.bookingFilter.numberOfAttendees, $scope.newBooking.ExternalNames.length);

        //Validate Subject
        var Subject = ValidateSubject($scope.newBooking.Subject);

        Booking.save({
            room: $scope.newBooking.RoomName,
            startDate: $scope.newBooking.StartDate,
            endDate: $scope.newBooking.EndDate,
            subject: Subject,
            attendeeEmails: Emails,
            externalNames: JSON.stringify($scope.newBooking.ExternalNames),
            pc: $scope.newBooking.Pc,
            flipchart: $scope.newBooking.FlipChart,
            projector: $scope.newBooking.Projector
        },
        function (success) {
            alert('Booking Saved');
        },
        function (error) {
            alert('Error');
        })

    }

    $scope.bookingFilter = {
        startDate: new moment().utc().format('DD-MM-YYYY'),
        startTime: '',
        endTime: '',
        location: $scope.currentLocation,
        smartRoom: false,
        numberOfAttendees: 1
    };

    $scope.booking = {
        StartDate: new Date(),
        EndDate: new Date(),
        Room: { RoomName: '' },
        Location: { LocationName: '' }
    };

    $scope.newBooking = {
        RoomName: '',
        Subject: '',
        AttendeeEmails: '',
        ExternalNames: [],
        StartDate: new Date(),
        EndDate: new Date(),
        Pc: false,
        FlipChart: false,
        Projector: false
    };
}

function CreateServices($resource) {
    Locations = $resource('/api/locations', {
    }, {
        query: { method: 'GET', isArray: true }
    });

    Available = $resource('/api/availability/:location/:startDate/:endDate/:numberOfAttendees/:smartRoom', { location: 'location', startDate: 'startDate', endDate: 'endDate', numberOfAttendees: 'numberOfAttendees', smartRoom: 'smartRoom' },
    {
        query: { method: 'GET', isArray: true }
    });

    Booking = $resource('/api/bookings/:room/:startDate/:endDate/:subject/:attendeeEmails/:externalNames/:pc/:flipchart/:projector', { room: 'room', startDate: 'startDate', endDate: 'endDate', subject: 'subject', attendeeEmails: 'attendeeEmails', externalNames: 'externalNames', pc: 'pc', flipchart: 'flipchart', projector: 'projector' },
    {
        save: { method: 'POST' }
    });
}

//Caladner UI Functions

function isMeetingOverlapping(event, calendar) {
    var array = calendar.clientEvents();
    for (i in array) {
        if (event.end > array[i].start && event.start < array[i].end) {
            return true;
        }
    }
    return false;
}

function InitiateCalendar() {
    $(function () {
        $('#calendar').fullCalendar({
            weekends: false,
            header: {
                left: 'prev,next today',
                center: 'title',
                right: 'month,agendaWeek,agendaDay'
            },
            events: function (start, end, timezone, callback) {
                var $scope = angular.element($("#controllerDiv")).scope();
                $.ajax({
                    url: '/api/bookings/' + moment.utc(start, "DD-MM-YYYY").format("MM-DD-YYYY") + '/' + moment.utc(end, "DD-MM-YYYY").format("MM-DD-YYYY") + '/' + $scope.currentRoom.roomName,
                    dataType: 'json',
                    type: 'POST',
                    contentType: 'application/json; charset=utf-8',
                    data: JSON.stringify($scope.currentLocation),
                    success: function (data) {
                        var events = [];
                        for (var i = 0; i < data.length; i++) {
                            events.push({
                                title: data[i].owner,
                                start: data[i].startDate,
                                end: data[i].endDate
                            });
                        }
                        callback(events);
                    }
                });

            }
        });
    });
}
///////////////////////////////////////////////////////////////////

//Format Functions

function convertTo24Hour(time) {
    var hours = parseInt(time.substr(0, 2));
    time = time.toString().trim();
    if (time.indexOf('AM') != -1 && hours == 12) {
        time = time.replace('12', '0');
    }
    if (time.indexOf('PM') != -1 && hours < 12) {
        time = time.replace(hours, (hours + 12));
    }
    return time.replace(/(AM|PM)/, '').slice(0, -1);
}

function FormatDateForURL(date) {

    if (date === "") {
        alert('Please Enter a Valid Date');
        throw new Error();
    }

    var parts = date.split('-');
    myDate2 = new Date(parts[2], parts[1] - 1, parts[0]);

    return moment(myDate2).format('MM-DD-YYYY');
}

function FormatTime(time, date) {
    if (time === "") {
        alert('Invalid Time');
        throw new Error();
    }

    var parts = date.split('-');
    var timeSplit = convertTo24Hour(time).split(':');

    if (timeSplit[0] > 17 || (timeSplit[0] == 17 && timeSplit[1] > 0)) {
        alert('Time Must Be Before 5.00 PM');
        throw new Error();
    }
    else if (timeSplit[0] < 9) {
        alert('Time Must Be After 9.00 AM');
        throw new Error();
    };

    var timeDate = new Date(parts[2], parts[1] - 1, parts[0]);
    timeDate.setHours(timeSplit[0], timeSplit[1], 0);

    return timeDate;
}
///////////////////////////////////////////////////////////////////

//New Booking Functions

function ValidateEmails(emails) {

    var attendeeEmails = null;

    if (emails.length < 1) {
        alert('No Emails Detected');
        throw new Error();
    }

    for (var i = 0; i < emails.length; i++) {
        //Validate Each Email
        if (emails[i].trim() === "" || !ValidateEmail(emails[i])) {
            alert('Invalid Email Detected: ' + emails[i]);
            throw new Error();
        }
        else {
            attendeeEmails += emails[i].trim() + ";"
        }
    }

    return attendeeEmails;
}

function ValidateNoAttendees(attendees, filterAttendees, externalNamesLength) {
    if (attendees !== filterAttendees) {
        alert('Number of Atteneds Does Not Match Number of Emails.');
        throw new Error();
    }
    else if (externalNamesLength > attendees) {
        alert('Too Many External names');
        throw new Error();
    }
}

function ValidateSubject(subject) {
    if (subject.trim() === "") {
        SetModalErrorMessage('Invalid Subject Detected');
        throw new Error();
    }
    return subject;
}

function ValidateEmail(email) {
    var regex = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i;
    return regex.test(email);
}

function SetModalErrorMessage(message) {
    if (message === "") {
        $('#newBookingErrorMessage').hide();
    }
    else {
        $('#newBookingErrorMessage').text(message);
        $('#newBookingErrorMessage').show();
    }
}

///////////////////////////////////////////////////////////////////

//Filtering Functions

function ValidateAttendess(attendees) {

    if (attendees === null) {
        throw new Error();
    }
    else if (isNaN(attendees)) {
        alert('Invalid Number');
        throw new Error();
    }
    else if (attendees <= 0) {
        alert('Attendees Can Not Be Negative');
        throw new Error();
    }
    return attendees;
}

function ValidateDates(start, end) {
    //ToDo: Add Equals then
    if (start > end) {
        alert('Start Date Can Not Be Ahead Of End Date');
        throw new Error();
    }
}

function IncrementTime(time, minutes) {
    var timeSplit = time.split(':');
    var amPm = timeSplit[1].split(" ");

    minutes = parseInt(amPm[0]) + minutes

    if (minutes >= 60) {
        minutes -= 60;
        ("0" + minutes).slice(-2);
    }

    return "'" + timeSplit[0] + ":" + minutes + " " + amPm[1] + "'";
}
///////////////////////////////////////////////////////////////////
<|endoftext|>