<commit_before>(function () {
"use strict";

var findScrollParent = function (el) {
	var ref = el;
	while (ref) {
		switch (window.getComputedStyle(ref).overflow) {
			case "auto":
				return ref;
			case "scroll":
				return ref;
		}
		ref = ref.parentElement;
	}
	return window;
};

var ScrollPagination = window.ScrollPagination = React.createClass({
	displayName: "ScrollPagination",

	getDefaultProps: function () {
		return {
			hasPrevPage: false,
			hasNextPage: false,
			loadPrevPage: function () {},
			loadNextPage: function () {}
		};
	},

	componentWillMount: function () {
		this.__pages = {};
		this.__pageIds = [];
		this.props.manager.registerPageEventHandler(this.handlePageEvent);
	},

	componentDidMount: function () {
		var scrollParent = this.__scrollParent = findScrollParent(this.getDOMNode());
		scrollParent.addEventListener("scroll", this.__handleScroll, false);
		scrollParent.addEventListener("resize", this.__handleResize, false);

		this.__updatePageIds();
		this.__updateDimensions();
	},

	componentDidUpdate: function () {
		this.__updatePageIds();
		this.__updateDimensions();

		this.__loadingNextPage = false;
		this.__loadingPrevPage = false;
		this.__unloadingPage = false;

		this.__evaluatePagesMutation();
	},

	componentWillUnmount: function () {
		var scrollParent = this.__scrollParent;
		scrollParent.removeEventListener("scroll", this.__handleScroll, false);
		scrollParent.removeEventListener("resize", this.__handleResize, false);
	},

	render: function () {
		var style = {};
		if (this.__paddingTop) {
			style.paddingTop = this.__paddingTop + "px";
		}
		return React.createElement('div', {
			style: style,
			ref: "wrapper"
		}, this.props.children);
	},

	// called from Page component via parent component
	handlePageEvent: function (pageId, event) {
		var pages = this.__pages;
		switch (event.name) {
			case "mount":
				pages[pageId] = {
					id: pageId,
					height: event.height
				};
			break;
		}
		this.__pages = pages;
	},

	__unloadPage: function (pageId) {
		if (this.__unloadingPage) {
			return;
		}
		this.__unloadingPage = true;
		this.props.unloadPage(pageId);
	},

	__loadPrevPage: function () {
		if (this.__loadingPrevPage || !this.props.hasPrevPage) {
			return;
		}
		this.__loadingPrevPage = true;
		this.props.loadPrevPage();
	},

	__loadNextPage: function () {
		if (this.__loadingNextPage || !this.props.hasNextPage) {
			return;
		}
		this.__loadingNextPage = true;
		this.props.loadNextPage();
	},

	__getScrollY: function () {
		var scrollParent = this.__scrollParent;
		if (scrollParent === window) {
			return window.scrollY;
		} else {
			return scrollParent.scrollTop;
		}
	},

	__setScrollY: function (scrollY) {
		var scrollParent = this.__scrollParent;
		if (scrollParent === window) {
			return window.scrollTo(window.scrollX, scrollY);
		} else {
			return scrollParent.scrollTop = scrollY;
		}
	},

	__updatePageIds: function () {
		var oldPageIds = this.__pageIds;
		var unloadedPageIdsTop = [];
		var newPageIdsTop = [];
		var pageIds = [];
		var findPages = function (children) {
			React.Children.forEach(children, function (child) {
				if (child === null) {
					return;
				}
				if (child.type.displayName === "ScrollPagination.Page") {
					pageIds.push(child.props.id);
				} else if (child.type === "ul") {
					findPages(child.props.children);
				}
			});
		};
		findPages(this.props.children);
		var i, len;
		if (oldPageIds.length > 0) {
			for (i = 0, len = oldPageIds.length; i < len; i++) {
				if (pageIds.indexOf(oldPageIds[i]) === -1) {
					unloadedPageIdsTop.push(oldPageIds[i]);
				} else {
					break;
				}
			}
			for (i = 0, len = pageIds.length; i < len; i++) {
				if (oldPageIds.indexOf(pageIds[i]) === -1) {
					newPageIdsTop.push(pageIds[i]);
				} else {
					break;
				}
			}
		}
		this.__adjustScrollPosition(unloadedPageIdsTop, newPageIdsTop);
		this.__pageIds = pageIds;
	},

	__adjustScrollPosition: function (unloadedPageIdsTop, newPageIdsTop) {
		var offset = 0;
		var pages = this.__pages;
		unloadedPageIdsTop.forEach(function (pageId) {
			var height = pages[pageId].height;
			offset += height;
			delete pages[pageId];
		});
		newPageIdsTop.forEach(function (pageId) {
			var height = pages[pageId].height;
			offset += height;
		});
		this.__setScrollY(this.__getScrollY() + offset);
	},

	__updateDimensions: function () {
		var el = this.refs.wrapper.getDOMNode();
		var scrollParent = this.__scrollParent;
		var viewportHeight = 0;
		if (scrollParent === window) {
			viewportHeight = window.innerHeight;
		} else {
			viewportHeight = parseInt(window.getComputedStyle(scrollParent).height, 10);
		}
		var contentHeight = el.offsetHeight;

		var offsetTop = 0;
		var ref = el;
		while (ref) {
			offsetTop += ref.offsetTop || 0;
			ref = ref.offsetParent;
			if (ref === scrollParent) {
				break;
			}
		}

		this.__dimentions = {
			viewportHeight: viewportHeight,
			contentHeight: contentHeight,
			offsetTop: offsetTop
		};
	},

	__evaluatePagesMutation: function (e) {
		if (this.__dimentions.contentHeight === 0) {
			return;
		}
		if (this.__loadingNextPage || this.__loadingPrevPage || this.__unloadingPage || !(this.props.hasNextPage || this.props.hasPrevPage)) {
			if (e) {
				e.preventDefault();
			}
			return;
		}

		var pages = this.__pages;
		var pageIds = this.__pageIds;
		var firstPage = pages[pageIds[0]];
		var secondPage = pages[pageIds[1]];
		var lastPage = pages[pageIds[pageIds.length-1]];
		var secondLastPage = pages[pageIds[pageIds.length-2]];

		if (pages.length < 4) {
			secondPage = null;
			secondLastPage = null;
		}

		var viewportHeight = this.__dimentions.viewportHeight;
		var contentHeight = this.__dimentions.contentHeight;
		var offsetTop = this.__dimentions.offsetTop;
		var scrollY = this.__getScrollY();

		var remainingScrollBottom = contentHeight - scrollY - viewportHeight + offsetTop;
		var remainingScrollTop = contentHeight - remainingScrollBottom - viewportHeight;

		if (lastPage && remainingScrollBottom < (lastPage.height / 3)) {
			if (secondPage && remainingScrollTop > (firstPage.height + secondPage.height)) {
				this.__unloadPage(firstPage.id);
			} else {
				this.__loadNextPage();
			}
		} else if (firstPage && remainingScrollTop < (firstPage.height / 3)) {
			if (secondLastPage && remainingScrollBottom > (lastPage.height + secondLastPage.height)) {
				this.__unloadPage(lastPage.id);
			} else {
				this.__loadPrevPage();
			}
		}
	},

	__handleScroll: function (e) {
		this.__evaluatePagesMutation(e);
	},

	__handleResize: function () {
		this.__updateDimensions();
		this.__evaluatePagesMutation();
	}
});

ScrollPagination.Page = React.createClass({
	displayName: "ScrollPagination.Page",

	getDefaultProps: function () {
		return {
			component: 'div'
		};
	},

	componentDidMount: function () {
		this.__determineHeight();
	},

	componentDidUpdate: function () {
		if (this.__height === 0) {
			this.__determineHeight();
		}
	},

	render: function () {
		var props = {};
		for (var k in this.props) {
			if (k !== "component" && k !== "onPageEvent" && k !== "id" && this.props.hasOwnProperty(k)) {
				props[k] = this.props[k];
			}
		}
		return React.createElement(this.props.component, props, this.props.children);
	},

	__determineHeight: function () {
		var height = this.__height = this.getDOMNode().offsetHeight;
		this.props.manager.dispatchPageEvent(this.props.id, {
			name: "mount",
			height: height
		});
	}
});

var Manager = function () {
	this.pendingEvents = [];
	this.pageEventHandler = null;
};
Manager.prototype.registerPageEventHandler = function (callback) {
	this.pageEventHandler = callback;
	this.pendingEvents.forEach(function (args) {
		this.dispatchPageEvent.apply(this, args);
	}.bind(this));
	this.pendingEvents = [];
};
Manager.prototype.dispatchPageEvent = function (pageId, event) {
	if (this.pageEventHandler === null) {
		this.pendingEvents.push([pageId, event]);
		return;
	}
	this.pageEventHandler(pageId, event);
};
ScrollPagination.Manager = Manager;

})();
<commit_msg>Add option to show new pages top when scrolled to top<commit_after>(function () {
"use strict";

var findScrollParent = function (el) {
	var ref = el;
	while (ref) {
		switch (window.getComputedStyle(ref).overflow) {
			case "auto":
				return ref;
			case "scroll":
				return ref;
		}
		ref = ref.parentElement;
	}
	return window;
};

var ScrollPagination = window.ScrollPagination = React.createClass({
	displayName: "ScrollPagination",

	getDefaultProps: function () {
		return {
			hasPrevPage: false,
			hasNextPage: false,
			loadPrevPage: function () {},
			loadNextPage: function () {}
		};
	},

	componentWillMount: function () {
		this.__pages = {};
		this.__pageIds = [];
		this.props.manager.registerPageEventHandler(this.handlePageEvent);
	},

	componentDidMount: function () {
		var scrollParent = this.__scrollParent = findScrollParent(this.getDOMNode());
		scrollParent.addEventListener("scroll", this.__handleScroll, false);
		scrollParent.addEventListener("resize", this.__handleResize, false);

		this.__updatePageIds();
		this.__updateDimensions();
	},

	componentDidUpdate: function () {
		this.__updatePageIds();
		this.__updateDimensions();

		this.__loadingNextPage = false;
		this.__loadingPrevPage = false;
		this.__unloadingPage = false;

		this.__evaluatePagesMutation();
	},

	componentWillUnmount: function () {
		var scrollParent = this.__scrollParent;
		scrollParent.removeEventListener("scroll", this.__handleScroll, false);
		scrollParent.removeEventListener("resize", this.__handleResize, false);
	},

	render: function () {
		var style = {};
		if (this.__paddingTop) {
			style.paddingTop = this.__paddingTop + "px";
		}
		return React.createElement('div', {
			style: style,
			ref: "wrapper"
		}, this.props.children);
	},

	// called from Page component via parent component
	handlePageEvent: function (pageId, event) {
		var pages = this.__pages;
		switch (event.name) {
			case "mount":
				pages[pageId] = {
					id: pageId,
					height: event.height
				};
			break;
		}
		this.__pages = pages;
	},

	__unloadPage: function (pageId) {
		if (this.__unloadingPage) {
			return;
		}
		this.__unloadingPage = true;
		this.props.unloadPage(pageId);
	},

	__loadPrevPage: function () {
		if (this.__loadingPrevPage || !this.props.hasPrevPage) {
			return;
		}
		this.__loadingPrevPage = true;
		this.props.loadPrevPage();
	},

	__loadNextPage: function () {
		if (this.__loadingNextPage || !this.props.hasNextPage) {
			return;
		}
		this.__loadingNextPage = true;
		this.props.loadNextPage();
	},

	__getScrollY: function () {
		var scrollParent = this.__scrollParent;
		if (scrollParent === window) {
			return window.scrollY;
		} else {
			return scrollParent.scrollTop;
		}
	},

	__setScrollY: function (scrollY) {
		var scrollParent = this.__scrollParent;
		if (scrollParent === window) {
			return window.scrollTo(window.scrollX, scrollY);
		} else {
			return scrollParent.scrollTop = scrollY;
		}
	},

	__updatePageIds: function () {
		var oldPageIds = this.__pageIds;
		var unloadedPageIdsTop = [];
		var newPageIdsTop = [];
		var pageIds = [];
		var findPages = function (children) {
			React.Children.forEach(children, function (child) {
				if (child === null) {
					return;
				}
				if (child.type.displayName === "ScrollPagination.Page") {
					pageIds.push(child.props.id);
				} else if (child.type === "ul") {
					findPages(child.props.children);
				}
			});
		};
		findPages(this.props.children);
		var i, len;
		if (oldPageIds.length > 0) {
			for (i = 0, len = oldPageIds.length; i < len; i++) {
				if (pageIds.indexOf(oldPageIds[i]) === -1) {
					unloadedPageIdsTop.push(oldPageIds[i]);
				} else {
					break;
				}
			}
			for (i = 0, len = pageIds.length; i < len; i++) {
				if (oldPageIds.indexOf(pageIds[i]) === -1) {
					newPageIdsTop.push(pageIds[i]);
				} else {
					break;
				}
			}
		}
		this.__adjustScrollPosition(unloadedPageIdsTop, newPageIdsTop);
		this.__pageIds = pageIds;
	},

	__adjustScrollPosition: function (unloadedPageIdsTop, newPageIdsTop) {
		var offset = 0;
		var pages = this.__pages;
		unloadedPageIdsTop.forEach(function (pageId) {
			var height = pages[pageId].height;
			offset += height;
			delete pages[pageId];
		});
		newPageIdsTop.forEach(function (pageId) {
			var height = pages[pageId].height;
			offset += height;
		});
		if (this.props.showNewItemsTop !== true || this.__scrollY !== 0) {
			this.__setScrollY(this.__getScrollY() + offset);
		}
	},

	__updateDimensions: function () {
		var el = this.refs.wrapper.getDOMNode();
		var scrollParent = this.__scrollParent;
		var viewportHeight = 0;
		if (scrollParent === window) {
			viewportHeight = window.innerHeight;
		} else {
			viewportHeight = parseInt(window.getComputedStyle(scrollParent).height, 10);
		}
		var contentHeight = el.offsetHeight;

		var offsetTop = 0;
		var ref = el;
		while (ref) {
			offsetTop += ref.offsetTop || 0;
			ref = ref.offsetParent;
			if (ref === scrollParent) {
				break;
			}
		}

		this.__dimentions = {
			viewportHeight: viewportHeight,
			contentHeight: contentHeight,
			offsetTop: offsetTop
		};
	},

	__evaluatePagesMutation: function (e) {
		if (this.__dimentions.contentHeight === 0) {
			return;
		}
		if (this.__loadingNextPage || this.__loadingPrevPage || this.__unloadingPage || !(this.props.hasNextPage || this.props.hasPrevPage)) {
			if (e) {
				e.preventDefault();
			}
			return;
		}

		var pages = this.__pages;
		var pageIds = this.__pageIds;
		var firstPage = pages[pageIds[0]];
		var secondPage = pages[pageIds[1]];
		var lastPage = pages[pageIds[pageIds.length-1]];
		var secondLastPage = pages[pageIds[pageIds.length-2]];

		if (pages.length < 4) {
			secondPage = null;
			secondLastPage = null;
		}

		var viewportHeight = this.__dimentions.viewportHeight;
		var contentHeight = this.__dimentions.contentHeight;
		var offsetTop = this.__dimentions.offsetTop;
		var scrollY = this.__scrollY = this.__getScrollY();

		var remainingScrollBottom = contentHeight - scrollY - viewportHeight + offsetTop;
		var remainingScrollTop = contentHeight - remainingScrollBottom - viewportHeight;

		if (lastPage && remainingScrollBottom < (lastPage.height / 3)) {
			if (secondPage && remainingScrollTop > (firstPage.height + secondPage.height)) {
				this.__unloadPage(firstPage.id);
			} else {
				this.__loadNextPage();
			}
		} else if (firstPage && remainingScrollTop < (firstPage.height / 3)) {
			if (secondLastPage && remainingScrollBottom > (lastPage.height + secondLastPage.height)) {
				this.__unloadPage(lastPage.id);
			} else {
				this.__loadPrevPage();
			}
		}
	},

	__handleScroll: function (e) {
		this.__evaluatePagesMutation(e);
	},

	__handleResize: function () {
		this.__updateDimensions();
		this.__evaluatePagesMutation();
	}
});

ScrollPagination.Page = React.createClass({
	displayName: "ScrollPagination.Page",

	getDefaultProps: function () {
		return {
			component: 'div'
		};
	},

	componentDidMount: function () {
		this.__determineHeight();
	},

	componentDidUpdate: function () {
		if (this.__height === 0) {
			this.__determineHeight();
		}
	},

	render: function () {
		var props = {};
		for (var k in this.props) {
			if (k !== "component" && k !== "onPageEvent" && k !== "id" && this.props.hasOwnProperty(k)) {
				props[k] = this.props[k];
			}
		}
		return React.createElement(this.props.component, props, this.props.children);
	},

	__determineHeight: function () {
		var height = this.__height = this.getDOMNode().offsetHeight;
		this.props.manager.dispatchPageEvent(this.props.id, {
			name: "mount",
			height: height
		});
	}
});

var Manager = function () {
	this.pendingEvents = [];
	this.pageEventHandler = null;
};
Manager.prototype.registerPageEventHandler = function (callback) {
	this.pageEventHandler = callback;
	this.pendingEvents.forEach(function (args) {
		this.dispatchPageEvent.apply(this, args);
	}.bind(this));
	this.pendingEvents = [];
};
Manager.prototype.dispatchPageEvent = function (pageId, event) {
	if (this.pageEventHandler === null) {
		this.pendingEvents.push([pageId, event]);
		return;
	}
	this.pageEventHandler(pageId, event);
};
ScrollPagination.Manager = Manager;

})();
<|endoftext|>


<commit_before>
/**
 * Module dependencies.
 */

var EventEmitter = require('events').EventEmitter
  , debug = require('debug')('runnable');

/**
 * Expose `Runnable`.
 */

module.exports = Runnable;

/**
 * Initialize a new `Runnable` with the given `title` and callback `fn`.
 *
 * @param {String} title
 * @param {Function} fn
 * @api private
 */

function Runnable(title, fn) {
  this.title = title;
  this.fn = fn;
  this.async = fn && fn.length;
  this.sync = ! this.async;
  this._timeout = 2000;
}

/**
 * Inherit from `EventEmitter.prototype`.
 */

Runnable.prototype.__proto__ = EventEmitter.prototype;

/**
 * Set & get timeout `ms`.
 *
 * @param {Number} ms
 * @return {Runnable|Number} ms or self
 * @api private
 */

Runnable.prototype.timeout = function(ms){
  if (0 == arguments.length) return this._timeout;
  debug('timeout %d', ms);
  this._timeout = ms;
  return this;
};

/**
 * Return the full title generated by recursively
 * concatenating the parent's full title.
 *
 * @return {String}
 * @api public
 */

Runnable.prototype.fullTitle = function(){
  return this.parent.fullTitle() + ' ' + this.title;
};

/**
 * Run the test and invoke `fn(err)`.
 *
 * @param {Function} fn
 * @api private
 */

Runnable.prototype.run = function(fn){
  var self = this
    , ms = this.timeout()
    , start = new Date
    , finished
    , emitted
    , timer;

  // timeout
  if (this.async) {
    timer = setTimeout(function(){
      done(new Error('timeout of ' + ms + 'ms exceeded'));
    }, ms);
  }

  // called multiple times
  function multiple() {
    if (emitted) return;
    emitted = true;
    self.emit('error', new Error('done() called multiple times'));
  }

  // finished
  function done(err) {
    if (finished) return multiple();
    clearTimeout(timer);
    self.duration = new Date - start;
    finished = true;
    fn(err);
  }

  // async
  if (this.async) {
    try {
      this.fn(function(err){
        if (err instanceof Error) return done(err);
        done();
      });
    } catch (err) {
      done(err);
    }
    return;
  }

  // sync
  try {
    if (!this.pending) this.fn();
    this.duration = new Date - start;
    fn();
  } catch (err) {
    fn(err);
  }
};
<commit_msg>Added Runnable#clearTimeout()<commit_after>
/**
 * Module dependencies.
 */

var EventEmitter = require('events').EventEmitter
  , debug = require('debug')('runnable');

/**
 * Expose `Runnable`.
 */

module.exports = Runnable;

/**
 * Initialize a new `Runnable` with the given `title` and callback `fn`.
 *
 * @param {String} title
 * @param {Function} fn
 * @api private
 */

function Runnable(title, fn) {
  this.title = title;
  this.fn = fn;
  this.async = fn && fn.length;
  this.sync = ! this.async;
  this._timeout = 2000;
}

/**
 * Inherit from `EventEmitter.prototype`.
 */

Runnable.prototype.__proto__ = EventEmitter.prototype;

/**
 * Set & get timeout `ms`.
 *
 * @param {Number} ms
 * @return {Runnable|Number} ms or self
 * @api private
 */

Runnable.prototype.timeout = function(ms){
  if (0 == arguments.length) return this._timeout;
  debug('timeout %d', ms);
  this._timeout = ms;
  return this;
};

/**
 * Return the full title generated by recursively
 * concatenating the parent's full title.
 *
 * @return {String}
 * @api public
 */

Runnable.prototype.fullTitle = function(){
  return this.parent.fullTitle() + ' ' + this.title;
};

/**
 * Clear the timeout.
 *
 * @api private
 */

Runnable.prototype.clearTimeout = function(){
  clearTimeout(this.timer);
};

/**
 * Run the test and invoke `fn(err)`.
 *
 * @param {Function} fn
 * @api private
 */

Runnable.prototype.run = function(fn){
  var self = this
    , ms = this.timeout()
    , start = new Date
    , finished
    , emitted;

  // timeout
  if (this.async) {
    this.timer = setTimeout(function(){
      done(new Error('timeout of ' + ms + 'ms exceeded'));
    }, ms);
  }

  // called multiple times
  function multiple() {
    if (emitted) return;
    emitted = true;
    self.emit('error', new Error('done() called multiple times'));
  }

  // finished
  function done(err) {
    if (finished) return multiple();
    self.clearTimeout();
    self.duration = new Date - start;
    finished = true;
    fn(err);
  }

  // async
  if (this.async) {
    try {
      this.fn(function(err){
        if (err instanceof Error) return done(err);
        done();
      });
    } catch (err) {
      done(err);
    }
    return;
  }

  // sync
  try {
    if (!this.pending) this.fn();
    this.duration = new Date - start;
    fn();
  } catch (err) {
    fn(err);
  }
};
<|endoftext|>


<commit_before>goog.provide('og.PointCloud');

goog.require('og.mercator');
goog.require('og.math.Vector3');
goog.require('og.math.Vector4');

/**
 * PointCloud object.
 * @class
 * @param {*} [options] - Point cloud options:
 * @param {Array.<Array.<number,number,number>>} [options.points] - Points cartesian coordinates array. Like path:[[0,0,0], [1,1,1],...]
 */
og.PointCloud = function (options) {

    options = options || {};

    /**
     * Object unic identifier.
     * @public
     * @readonly
     * @type {number}
     */
    this.id = og.PointCloud.__staticId++;

    /**
     * Cloud visibility.
     * @public
     * @type {boolean}
     */
    this.visibility = (options.visibility != undefined ? options.visibility : true);

    /**
     * Parent collection render node.
     * @private
     * @type {og.scene.RenderNode}
     */
    this._renderNode = null;

    /**
     * Entity instance that holds this point cloud.
     * @private
     * @type {og.Entity}
     */
    this._entity = null;

    /**
     * Points properties.
     * @private
     * @type {Array.<*>}
     */
    this._points = [];

    this._coordinatesData = [];
    this._colorData = [];
    this._pickingColorData = [];

    this._coordinatesBuffer = null;
    this._colorBuffer = null;
    this._pickingColorBuffer = null;

    /**
     * Handler that stores and renders this linestring object.
     * @private
     * @type {og.PointCloudHandler}
     */
    this._handler = null;
    this._handlerIndex = -1;

    this._buffersUpdateCallbacks = [];
    this._buffersUpdateCallbacks[og.PointCloud.COORDINATES_BUFFER] = this._createCoordinatesBuffer;
    this._buffersUpdateCallbacks[og.PointCloud.COLOR_BUFFER] = this._createColorBuffer;

    this._changedBuffers = new Array(this._buffersUpdateCallbacks.length);

    this.setPoints(options.points);
};

og.PointCloud.COORDINATES_BUFFER = 0;
og.PointCloud.COLOR_BUFFER = 1;

og.PointCloud.__staticId = 0;

og.PointCloud.prototype.clear = function () {

    this._points.length = 0;
    this._points = [];

    this._coordinatesData.length = 0;
    this._coordinatesData = [];

    this._colorData.length = 0;
    this._colorData = [];

    this._pickingColorData.length = 0;
    this._pickingColorData = [];

    this._deleteBuffers();
};

og.PointCloud.prototype._update = function () {
    if (this._renderNode) {
        var i = this._changedBuffers.length;
        while (i--) {
            if (this._changedBuffers[i]) {
                this._buffersUpdateCallbacks[i].call(this);
                this._changedBuffers[i] = false;
            }
        }
    }
};

og.PointCloud.prototype._deleteBuffers = function () {
    var r = this._renderNode.renderer,
        gl = r.handler.gl;

    gl.deleteBuffer(this._coordinatesBuffer);
    gl.deleteBuffer(this._colorBuffer);
    gl.deleteBuffer(this._pickingColorBuffer);

    this._coordinatesBuffer = null;
    this._colorBuffer = null;
    this._pickingColorBuffer = null;
};

og.PointCloud.prototype.setOpacity = function (opacity) {
    this.opacity = opacity;
};

og.PointCloud.prototype.setVisibility = function (visibility) {
    this.visibility = visibility;
};

og.PointCloud.prototype.getVisibility = function () {
    return this.visibility;
};

og.PointCloud.prototype.setRenderNode = function (renderNode) {
    this._renderNode = renderNode;
    this.setPickingColors();
};

og.PointCloud.prototype.remove = function () {
    this._entity = null;
    this._handler && this._handler.remove(this);
};

og.PointCloud.prototype._createCoordinatesBuffer = function () {
    var h = this._renderNode.renderer.handler;
    h.gl.deleteBuffer(this._coordinatesBuffer);
    this._coordinatesBuffer = h.createArrayBuffer(new Float32Array(this._coordinatesData), 3, (this._coordinatesData.length) / 3);
};

og.PointCloud.prototype._createColorBuffer = function () {
    var h = this._renderNode.renderer.handler;
    h.gl.deleteBuffer(this._colorBuffer);
    this._colorBuffer = h.createArrayBuffer(new Float32Array(this._colorData), 4, (this._colorData.length) / 4);
};

og.PointCloud.prototype._createPickingColorBuffer = function () {
    var h = this._renderNode.renderer.handler;
    h.gl.deleteBuffer(this._pickingColorBuffer);
    this._pickingColorBuffer = h.createArrayBuffer(new Float32Array(this._pickingColorData), 4, (this._pickingColorData.length) / 4);
};

og.PointCloud.prototype.setPickingColors = function () {
    if (this._renderNode) {
        for (var i = 0; i < this._points.length; i++) {
            var p = this._points[i];
            this._renderNode.renderer.assignPickingColor(p);
            this._pickingColorData.push(p._pickingColor.x, p._pickingColor.y, p._pickingColor.z, 1.0);
        }

        this._createPickingColorBuffer();
    }
};

//
//[[x,y,z,r,g,b,a,{}], [x,y,z,r,g,b,a], [x,y,z], ...]
og.PointCloud.prototype.setPoints = function (points) {
    for (var i = 0; i < points.length; i++) {
        var p = points[i];
        var pos = new og.math.Vector3(p[0], p[1], p[2]),
            col = new og.math.Vector4(p[3], p[4], p[5], p[6]);
        this._coordinatesData.push(pos.x, pos.y, pos.z);
        this._colorData.push(col.x / 255.0, col.y / 255.0, col.z / 255.0, col.w / 255.0);
        this._points.push({
            '_pickingColor': new og.math.Vector3(),
            'index': i,
            'position': pos,
            'color': col,
            'pointCloud': this,
            'properties': p[7]
        });
    }

    this._changedBuffers[og.PointCloud.COORDINATES_BUFFER] = true;
    this._changedBuffers[og.PointCloud.COLOR_BUFFER] = true;
};

og.PointCloud.prototype.setPoint3v = function (index, point) {

};

og.PointCloud.prototype.getPoint = function (index) {

};

og.PointCloud.prototype.removePoint = function (index) {

};

og.PointCloud.prototype.insertPoint = function (index, point) {

};

og.PointCloud.prototype.each = function (callback) {

};

og.PointCloud.prototype.draw = function () {
    if (this.visibility && this._coordinatesData.length) {

        this._update();

        var rn = this._renderNode;
        var r = rn.renderer;
        var sh = r.handler.shaderPrograms.pointCloud;
        var p = sh._program;
        var gl = r.handler.gl,
            sha = p.attributes,
            shu = p.uniforms;

        sh.activate();

        gl.uniformMatrix4fv(shu.projectionViewMatrix._pName, false, r.activeCamera._projectionViewMatrix._m);

        gl.uniform1f(shu.opacity._pName, this._handler._entityCollection._animatedOpacity);

        gl.bindBuffer(gl.ARRAY_BUFFER, this._coordinatesBuffer);
        gl.vertexAttribPointer(sha.coordinates._pName, this._coordinatesBuffer.itemSize, gl.FLOAT, false, 0, 0);

        gl.bindBuffer(gl.ARRAY_BUFFER, this._colorBuffer);
        gl.vertexAttribPointer(sha.colors._pName, this._colorBuffer.itemSize, gl.FLOAT, false, 0, 0);

        gl.drawArrays(gl.POINTS, 0, this._coordinatesBuffer.numItems);
    }
};

og.PointCloud.prototype.drawPicking = function () {
    if (this.visibility && this._coordinatesData.length) {
        var rn = this._renderNode;
        var r = rn.renderer;
        var sh = r.handler.shaderPrograms.pointCloud;
        var p = sh._program;
        var gl = r.handler.gl,
            sha = p.attributes,
            shu = p.uniforms;

        sh.activate();

        gl.uniformMatrix4fv(shu.projectionViewMatrix._pName, false, r.activeCamera._projectionViewMatrix._m);

        gl.uniform1f(shu.opacity._pName, this._handler._entityCollection._animatedOpacity);

        gl.bindBuffer(gl.ARRAY_BUFFER, this._coordinatesBuffer);
        gl.vertexAttribPointer(sha.coordinates._pName, this._coordinatesBuffer.itemSize, gl.FLOAT, false, 0, 0);

        gl.bindBuffer(gl.ARRAY_BUFFER, this._pickingColorBuffer);
        gl.vertexAttribPointer(sha.colors._pName, this._pickingColorBuffer.itemSize, gl.FLOAT, false, 0, 0);

        gl.drawArrays(gl.POINTS, 0, this._coordinatesBuffer.numItems);
    }
};<commit_msg>PointCloud point picking event bug fix.<commit_after>goog.provide('og.PointCloud');

goog.require('og.mercator');
goog.require('og.math.Vector3');
goog.require('og.math.Vector4');

/**
 * PointCloud object.
 * @class
 * @param {*} [options] - Point cloud options:
 * @param {Array.<Array.<number,number,number>>} [options.points] - Points cartesian coordinates array. Like path:[[0,0,0], [1,1,1],...]
 */
og.PointCloud = function (options) {

    options = options || {};

    /**
     * Object unic identifier.
     * @public
     * @readonly
     * @type {number}
     */
    this.id = og.PointCloud.__staticId++;

    /**
     * Cloud visibility.
     * @public
     * @type {boolean}
     */
    this.visibility = (options.visibility != undefined ? options.visibility : true);

    /**
     * Parent collection render node.
     * @private
     * @type {og.scene.RenderNode}
     */
    this._renderNode = null;

    /**
     * Entity instance that holds this point cloud.
     * @private
     * @type {og.Entity}
     */
    this._entity = null;

    /**
     * Points properties.
     * @private
     * @type {Array.<*>}
     */
    this._points = [];

    this._coordinatesData = [];
    this._colorData = [];
    this._pickingColorData = [];

    this._coordinatesBuffer = null;
    this._colorBuffer = null;
    this._pickingColorBuffer = null;

    /**
     * Handler that stores and renders this linestring object.
     * @private
     * @type {og.PointCloudHandler}
     */
    this._handler = null;
    this._handlerIndex = -1;

    this._buffersUpdateCallbacks = [];
    this._buffersUpdateCallbacks[og.PointCloud.COORDINATES_BUFFER] = this._createCoordinatesBuffer;
    this._buffersUpdateCallbacks[og.PointCloud.COLOR_BUFFER] = this._createColorBuffer;

    this._changedBuffers = new Array(this._buffersUpdateCallbacks.length);

    this.setPoints(options.points);
};

og.PointCloud.COORDINATES_BUFFER = 0;
og.PointCloud.COLOR_BUFFER = 1;

og.PointCloud.__staticId = 0;

og.PointCloud.prototype.clear = function () {

    this._points.length = 0;
    this._points = [];

    this._coordinatesData.length = 0;
    this._coordinatesData = [];

    this._colorData.length = 0;
    this._colorData = [];

    this._pickingColorData.length = 0;
    this._pickingColorData = [];

    this._deleteBuffers();
};

og.PointCloud.prototype._update = function () {
    if (this._renderNode) {
        var i = this._changedBuffers.length;
        while (i--) {
            if (this._changedBuffers[i]) {
                this._buffersUpdateCallbacks[i].call(this);
                this._changedBuffers[i] = false;
            }
        }
    }
};

og.PointCloud.prototype._deleteBuffers = function () {
    var r = this._renderNode.renderer,
        gl = r.handler.gl;

    gl.deleteBuffer(this._coordinatesBuffer);
    gl.deleteBuffer(this._colorBuffer);
    gl.deleteBuffer(this._pickingColorBuffer);

    this._coordinatesBuffer = null;
    this._colorBuffer = null;
    this._pickingColorBuffer = null;
};

og.PointCloud.prototype.setOpacity = function (opacity) {
    this.opacity = opacity;
};

og.PointCloud.prototype.setVisibility = function (visibility) {
    this.visibility = visibility;
};

og.PointCloud.prototype.getVisibility = function () {
    return this.visibility;
};

og.PointCloud.prototype.setRenderNode = function (renderNode) {
    this._renderNode = renderNode;
    this.setPickingColors();
};

og.PointCloud.prototype.remove = function () {
    this._entity = null;
    this._handler && this._handler.remove(this);
};

og.PointCloud.prototype._createCoordinatesBuffer = function () {
    var h = this._renderNode.renderer.handler;
    h.gl.deleteBuffer(this._coordinatesBuffer);
    this._coordinatesBuffer = h.createArrayBuffer(new Float32Array(this._coordinatesData), 3, (this._coordinatesData.length) / 3);
};

og.PointCloud.prototype._createColorBuffer = function () {
    var h = this._renderNode.renderer.handler;
    h.gl.deleteBuffer(this._colorBuffer);
    this._colorBuffer = h.createArrayBuffer(new Float32Array(this._colorData), 4, (this._colorData.length) / 4);
};

og.PointCloud.prototype._createPickingColorBuffer = function () {
    var h = this._renderNode.renderer.handler;
    h.gl.deleteBuffer(this._pickingColorBuffer);
    this._pickingColorBuffer = h.createArrayBuffer(new Float32Array(this._pickingColorData), 4, (this._pickingColorData.length) / 4);
};

og.PointCloud.prototype.setPickingColors = function () {
    if (this._renderNode) {
        for (var i = 0; i < this._points.length; i++) {
            var p = this._points[i];
            p._entityCollection = this._entity._entityCollection;
            this._renderNode.renderer.assignPickingColor(p);
            this._pickingColorData.push(p._pickingColor.x / 255.0, p._pickingColor.y / 255.0, p._pickingColor.z / 255.0, 1.0);
        }
        this._createPickingColorBuffer();
    }
};

//
//[[x,y,z,r,g,b,a,{}], [x,y,z,r,g,b,a], [x,y,z], ...]
og.PointCloud.prototype.setPoints = function (points) {
    for (var i = 0; i < points.length; i++) {
        var p = points[i];
        var pos = new og.math.Vector3(p[0], p[1], p[2]),
            col = new og.math.Vector4(p[3], p[4], p[5], p[6]);
        this._coordinatesData.push(pos.x, pos.y, pos.z);
        this._colorData.push(col.x / 255.0, col.y / 255.0, col.z / 255.0, col.w / 255.0);
        this._points.push({
            '_pickingColor': new og.math.Vector3(),
            '_entityCollection': this._entity && this._entity._entityCollection,
            'index': i,
            'position': pos,
            'color': col,
            'pointCloud': this,
            'properties': p[7]
        });
    }

    this._changedBuffers[og.PointCloud.COORDINATES_BUFFER] = true;
    this._changedBuffers[og.PointCloud.COLOR_BUFFER] = true;
};

og.PointCloud.prototype.setPoint3v = function (index, point) {

};

og.PointCloud.prototype.getPoint = function (index) {

};

og.PointCloud.prototype.removePoint = function (index) {

};

og.PointCloud.prototype.insertPoint = function (index, point) {

};

og.PointCloud.prototype.each = function (callback) {

};

og.PointCloud.prototype.draw = function () {
    if (this.visibility && this._coordinatesData.length) {

        this._update();

        var rn = this._renderNode;
        var r = rn.renderer;
        var sh = r.handler.shaderPrograms.pointCloud;
        var p = sh._program;
        var gl = r.handler.gl,
            sha = p.attributes,
            shu = p.uniforms;

        sh.activate();

        gl.uniformMatrix4fv(shu.projectionViewMatrix._pName, false, r.activeCamera._projectionViewMatrix._m);

        gl.uniform1f(shu.opacity._pName, this._handler._entityCollection._animatedOpacity);

        gl.bindBuffer(gl.ARRAY_BUFFER, this._coordinatesBuffer);
        gl.vertexAttribPointer(sha.coordinates._pName, this._coordinatesBuffer.itemSize, gl.FLOAT, false, 0, 0);

        gl.bindBuffer(gl.ARRAY_BUFFER, this._colorBuffer);
        gl.vertexAttribPointer(sha.colors._pName, this._colorBuffer.itemSize, gl.FLOAT, false, 0, 0);

        gl.drawArrays(gl.POINTS, 0, this._coordinatesBuffer.numItems);
    }
};

og.PointCloud.prototype.drawPicking = function () {
    if (this.visibility && this._coordinatesData.length) {
        var rn = this._renderNode;
        var r = rn.renderer;
        var sh = r.handler.shaderPrograms.pointCloud;
        var p = sh._program;
        var gl = r.handler.gl,
            sha = p.attributes,
            shu = p.uniforms;

        sh.activate();

        gl.uniformMatrix4fv(shu.projectionViewMatrix._pName, false, r.activeCamera._projectionViewMatrix._m);

        gl.uniform1f(shu.opacity._pName, this._handler._entityCollection._animatedOpacity);

        gl.bindBuffer(gl.ARRAY_BUFFER, this._coordinatesBuffer);
        gl.vertexAttribPointer(sha.coordinates._pName, this._coordinatesBuffer.itemSize, gl.FLOAT, false, 0, 0);

        gl.bindBuffer(gl.ARRAY_BUFFER, this._pickingColorBuffer);
        gl.vertexAttribPointer(sha.colors._pName, this._pickingColorBuffer.itemSize, gl.FLOAT, false, 0, 0);

        gl.drawArrays(gl.POINTS, 0, this._coordinatesBuffer.numItems);
    }
};<|endoftext|>


<commit_before>function LTmap() {

	// Singleton
	if (typeof LTmap.instance === 'object') {
		return LTmap.instance;
	}
	LTmap.instance = this;

	google.maps.Circle.prototype.contains = function(latLng) {
		return this.getBounds().contains(latLng)
				&& google.maps.geometry.spherical.computeDistanceBetween(this
						.getCenter(), latLng) <= this.getRadius();
	};

	// Defaults
	LTmap.elementId = 'map';
	LTmap.lat = '53.147086';
	LTmap.lng = '8.180434';
	LTmap.zoom = 4;
	LTmap.markerUnread = {
		url : 'TwitterBlue.png',
		size : new google.maps.Size(43, 36),
		origin : new google.maps.Point(0, 0),
		anchor : new google.maps.Point(21, 18)
	};
	LTmap.markerRead = {
		url : 'TwitterDark.png',
		size : new google.maps.Size(43, 36),
		origin : new google.maps.Point(0, 0),
		anchor : new google.maps.Point(21, 18)
	};
	LTmap.markerSpiderfiedUnread = {
		url : 'TwitterRedBlue.png',
		size : new google.maps.Size(43, 36),
		origin : new google.maps.Point(0, 0),
		anchor : new google.maps.Point(21, 18)
	};
	LTmap.markerSpiderfiedRead = {
		url : 'TwitterRedDark.png',
		size : new google.maps.Size(43, 36),
		origin : new google.maps.Point(0, 0),
		anchor : new google.maps.Point(21, 18)
	};
	LTmap.markerShape = {
		coords : [ 0, 33, 7, 36, 22, 36, 31, 31, 38, 22, 39, 19, 39, 12, 43, 8,
				43, 0, 24, 0, 22, 1, 19, 5, 18, 8, 17, 9, 15, 7, 13, 7, 5, 1,
				3, 1, 1, 4, 1, 17, 4, 21, 4, 24, 7, 26, 5, 28, 1, 28, 0, 29 ],
		type : 'poly'
	};

	var firstSetLocation = true;
	var center = new google.maps.LatLng(LTmap.lat, LTmap.lng);
	var circle = null;
	var openedMarker = null;
	var onMap = {};

	// Initialize map with defaults
	var map = new google.maps.Map(document.getElementById(LTmap.elementId), {
		zoom : LTmap.zoom,
		center : new google.maps.LatLng(LTmap.lat, LTmap.lng),
		mapTypeId : google.maps.MapTypeId.HYBRID,
		minZoom : 4,
		maxZoom : 18,
		zoomControlOptions : {
			position : google.maps.ControlPosition.RIGHT_TOP,
		},
		streetViewControlOptions : {
			position : google.maps.ControlPosition.RIGHT_TOP,
		},
		panControlOptions : {
			position : google.maps.ControlPosition.RIGHT_TOP,
		},
	});
	var oms = new OverlappingMarkerSpiderfier(map, {
		nearbyDistance : 40,
		circleFootSeparation : 40,
		spiralFootSeparation : 35,
		spiralLengthFactor : 7,
		spiralLengthStart : 12
	});
	oms.addListener('click', function(marker, event) {
		marker.openclick();
	});
	oms.addListener('spiderfy', function(markers) {
		for(var key in markers)
			if(markers[key].isRead)
				markers[key].setIcon(LTmap.markerSpiderfiedRead);
			else
				markers[key].setIcon(LTmap.markerSpiderfiedUnread);
	});
	oms.addListener('unspiderfy', function(markers) {
		for(var key in markers)
			if(markers[key].isRead)
				markers[key].setIcon(LTmap.markerRead);
			else
				markers[key].setIcon(LTmap.markerUnread);
	});

	var checkAddMarker = function(tweet) {
		if (tweet.coordinates && tweet.coordinates.type
				&& tweet.coordinates.type == 'Point'
				&& tweet.coordinates.coordinates
				&& tweet.coordinates.coordinates[1]
				&& tweet.coordinates.coordinates[0]) {
			if (circle.contains(new google.maps.LatLng(
					tweet.coordinates.coordinates[1],
					tweet.coordinates.coordinates[0]))
					&& new Date(tweet.created_at) > new Date(new Date()
							- new TimeFrame().getTime())) {
				return true;
			} else
				return false;
		} else
			return false;
	};

	var removeMessage = function(id_str) {
		// Never remove open markers or markers which should not be removed
		if (onMap[id_str].marker.isOpen || checkAddMarker(onMap[id_str].tweet))
			return;
		onMap[id_str].marker.setMap(null);
		delete onMap[id_str];
	};

	this.cleanMarkers = function() {
		for ( var id_str in onMap)
			removeMessage(id_str);
	};

	this.addMessage = function(tweet) {
		// Never add markers which should not be added or markers which already
		// exist
		if (!checkAddMarker(tweet) || onMap.hasOwnProperty(tweet.id_str))
			return;

		var isRead = new MessageReadStorage().messageRead(tweet.id_str);
		var icon = isRead ? LTmap.markerRead : LTmap.markerUnread;
		var marker = new google.maps.Marker({
			position : new google.maps.LatLng(tweet.coordinates.coordinates[1],
					tweet.coordinates.coordinates[0]),
			map : map,
			title : 'Zum Öffnen klicken!',
			icon : icon,
			shape : LTmap.markerShape,
			zIndex : 1
		});
		oms.addMarker(marker);
		marker.isRead = isRead;

		marker.message = new google.maps.InfoWindow({
			content : new TweetParser(tweet).parse(),
			maxWidth : 150
		});

		marker.isOpen = false;

		marker.closeclick = function() {
			openedMarker = null;
			marker.isOpen = false;
			// Remove from map when under certain conditions
			removeMessage(tweet.id_str);
			marker.message.close();
		};

		marker.openclick = function() {
			if (openedMarker != null)
				openedMarker.closeclick();
			marker.isOpen = true;
			openedMarker = marker;
			marker.message.open(map, marker);
			marker.setIcon(LTmap.markerRead);
			new MessageReadStorage().addMessage(tweet);
			marker.isRead = true;
		};

		google.maps.event.addListener(marker.message, 'closeclick',
				marker.closeclick);

		onMap[tweet.id_str] = {
			marker : marker,
			tweet : tweet
		};

		if (!marker.isRead && openedMarker == null)
			marker.openclick();
	};

	this.setLocation = function(lat, lng) {
		center = new google.maps.LatLng(lat, lng);
		if (firstSetLocation) {
			firstSetLocation = false;
			new Overlay().show('loadmap');
			map.setCenter(center);
			circle = new google.maps.Circle({
				strokeColor : '#00f',
				strokeOpacity : 0.8,
				strokeWeight : 4,
				fillColor : '#00f',
				fillOpacity : 0,
				map : map,
				center : center,
				radius : new DistanceFrame().getDistance(),
				clickable : false
			});
			circle.dot = new google.maps.Circle({
				strokeColor : '#f00',
				strokeOpacity : 0.8,
				strokeWeight : 20,
				fillColor : '#f00',
				fillOpacity : 0,
				map : map,
				center : center,
				radius : 1,
				clickable : false
			});
			map.fitBounds(circle.getBounds());
			map.setZoom(map.getZoom() + 1);
			google.maps.event.addListenerOnce(map, 'idle', function() {
				google.maps.event.addListenerOnce(map, 'tilesloaded',
						function() {
							new Overlay().hide();
						});
			});
		} else {
			circle.setCenter(center);
			circle.dot.setCenter(center);
		}
		this.update();
	};

	this.update = function() {
		if(circle == null)
			return;
		this.cleanMarkers();
		var bounds = circle.getBounds();
		var northEast = bounds.getNorthEast();
		var southWest = bounds.getSouthWest();
		var boundaries = {
			north : northEast.lat(),
			east : northEast.lng(),
			south : southWest.lat(),
			west : southWest.lng()
		};
		new Websocket().send(boundaries);
	};

	this.setRadius = function(meter) {
		if(circle == null)
			return;
		circle.setRadius(meter);
	};

	this.getGoogleMap = function() {
		return map;
	};

	this.getGoogleCircle = function() {
		return circle;
	};

	setInterval(this.cleanMarkers, 1000);

}
<commit_msg>Time parsing<commit_after>function LTmap() {

	// Singleton
	if (typeof LTmap.instance === 'object') {
		return LTmap.instance;
	}
	LTmap.instance = this;

	google.maps.Circle.prototype.contains = function(latLng) {
		return this.getBounds().contains(latLng)
				&& google.maps.geometry.spherical.computeDistanceBetween(this
						.getCenter(), latLng) <= this.getRadius();
	};

	// Defaults
	LTmap.elementId = 'map';
	LTmap.lat = '53.147086';
	LTmap.lng = '8.180434';
	LTmap.zoom = 4;
	LTmap.markerUnread = {
		url : 'TwitterBlue.png',
		size : new google.maps.Size(43, 36),
		origin : new google.maps.Point(0, 0),
		anchor : new google.maps.Point(21, 18)
	};
	LTmap.markerRead = {
		url : 'TwitterDark.png',
		size : new google.maps.Size(43, 36),
		origin : new google.maps.Point(0, 0),
		anchor : new google.maps.Point(21, 18)
	};
	LTmap.markerSpiderfiedUnread = {
		url : 'TwitterRedBlue.png',
		size : new google.maps.Size(43, 36),
		origin : new google.maps.Point(0, 0),
		anchor : new google.maps.Point(21, 18)
	};
	LTmap.markerSpiderfiedRead = {
		url : 'TwitterRedDark.png',
		size : new google.maps.Size(43, 36),
		origin : new google.maps.Point(0, 0),
		anchor : new google.maps.Point(21, 18)
	};
	LTmap.markerShape = {
		coords : [ 0, 33, 7, 36, 22, 36, 31, 31, 38, 22, 39, 19, 39, 12, 43, 8,
				43, 0, 24, 0, 22, 1, 19, 5, 18, 8, 17, 9, 15, 7, 13, 7, 5, 1,
				3, 1, 1, 4, 1, 17, 4, 21, 4, 24, 7, 26, 5, 28, 1, 28, 0, 29 ],
		type : 'poly'
	};

	var firstSetLocation = true;
	var center = new google.maps.LatLng(LTmap.lat, LTmap.lng);
	var circle = null;
	var openedMarker = null;
	var onMap = {};

	// Initialize map with defaults
	var map = new google.maps.Map(document.getElementById(LTmap.elementId), {
		zoom : LTmap.zoom,
		center : new google.maps.LatLng(LTmap.lat, LTmap.lng),
		mapTypeId : google.maps.MapTypeId.HYBRID,
		minZoom : 4,
		maxZoom : 18,
		zoomControlOptions : {
			position : google.maps.ControlPosition.RIGHT_TOP,
		},
		streetViewControlOptions : {
			position : google.maps.ControlPosition.RIGHT_TOP,
		},
		panControlOptions : {
			position : google.maps.ControlPosition.RIGHT_TOP,
		},
	});
	var oms = new OverlappingMarkerSpiderfier(map, {
		nearbyDistance : 40,
		circleFootSeparation : 40,
		spiralFootSeparation : 35,
		spiralLengthFactor : 7,
		spiralLengthStart : 12
	});
	oms.addListener('click', function(marker, event) {
		marker.openclick();
	});
	oms.addListener('spiderfy', function(markers) {
		for(var key in markers)
			if(markers[key].isRead)
				markers[key].setIcon(LTmap.markerSpiderfiedRead);
			else
				markers[key].setIcon(LTmap.markerSpiderfiedUnread);
	});
	oms.addListener('unspiderfy', function(markers) {
		for(var key in markers)
			if(markers[key].isRead)
				markers[key].setIcon(LTmap.markerRead);
			else
				markers[key].setIcon(LTmap.markerUnread);
	});

	var checkAddMarker = function(tweet) {
		if (tweet.coordinates && tweet.coordinates.type
				&& tweet.coordinates.type == 'Point'
				&& tweet.coordinates.coordinates
				&& tweet.coordinates.coordinates[1]
				&& tweet.coordinates.coordinates[0]) {
			if (circle.contains(new google.maps.LatLng(
					tweet.coordinates.coordinates[1],
					tweet.coordinates.coordinates[0]))
					&& new Date(tweet.created_at) > new Date(new Date()
							- new TimeFrame().getTime())) {
				return true;
			} else
				return false;
		} else
			return false;
	};

	var removeMessage = function(id_str) {
		// Never remove open markers or markers which should not be removed
		if (onMap[id_str].marker.isOpen || checkAddMarker(onMap[id_str].tweet))
			return;
		onMap[id_str].marker.setMap(null);
		delete onMap[id_str];
	};

	var updateTime = function() {
		if(openedMarker == null)
			return;
		var content = openedMarker.message.content;
		$('div.tweetDateTime', content).html('vor ' + TimeFrame.parse(new Date() - new Date(openedMarker.tweet.created_at)));
	};

	this.cleanMarkers = function() {
		for ( var id_str in onMap)
			removeMessage(id_str);
	};

	this.addMessage = function(tweet) {
		// Never add markers which should not be added or markers which already
		// exist
		if (!checkAddMarker(tweet) || onMap.hasOwnProperty(tweet.id_str))
			return;

		var isRead = new MessageReadStorage().messageRead(tweet.id_str);
		var icon = isRead ? LTmap.markerRead : LTmap.markerUnread;
		var marker = new google.maps.Marker({
			position : new google.maps.LatLng(tweet.coordinates.coordinates[1],
					tweet.coordinates.coordinates[0]),
			map : map,
			title : 'Zum Öffnen klicken!',
			icon : icon,
			shape : LTmap.markerShape,
			zIndex : 1
		});
		oms.addMarker(marker);
		marker.isRead = isRead;

		marker.tweet = tweet;
		marker.message = new google.maps.InfoWindow({
			content : new TweetParser(tweet).parse(),
			maxWidth : 150
		});

		marker.isOpen = false;

		marker.closeclick = function() {
			openedMarker = null;
			marker.isOpen = false;
			// Remove from map when under certain conditions
			removeMessage(tweet.id_str);
			marker.message.close();
		};

		marker.openclick = function() {
			if (openedMarker != null)
				openedMarker.closeclick();
			marker.isOpen = true;
			openedMarker = marker;
			updateTime();
			marker.message.open(map, marker);
			marker.setIcon(LTmap.markerRead);
			new MessageReadStorage().addMessage(tweet);
			marker.isRead = true;
		};

		google.maps.event.addListener(marker.message, 'closeclick',
				marker.closeclick);

		onMap[tweet.id_str] = {
			marker : marker,
			tweet : tweet
		};

		if (!marker.isRead && openedMarker == null)
			marker.openclick();
	};

	this.setLocation = function(lat, lng) {
		center = new google.maps.LatLng(lat, lng);
		if (firstSetLocation) {
			firstSetLocation = false;
			new Overlay().show('loadmap');
			map.setCenter(center);
			circle = new google.maps.Circle({
				strokeColor : '#00f',
				strokeOpacity : 0.8,
				strokeWeight : 4,
				fillColor : '#00f',
				fillOpacity : 0,
				map : map,
				center : center,
				radius : new DistanceFrame().getDistance(),
				clickable : false
			});
			circle.dot = new google.maps.Circle({
				strokeColor : '#f00',
				strokeOpacity : 0.8,
				strokeWeight : 20,
				fillColor : '#f00',
				fillOpacity : 0,
				map : map,
				center : center,
				radius : 1,
				clickable : false
			});
			map.fitBounds(circle.getBounds());
			map.setZoom(map.getZoom() + 1);
			google.maps.event.addListenerOnce(map, 'idle', function() {
				google.maps.event.addListenerOnce(map, 'tilesloaded',
						function() {
							new Overlay().hide();
						});
			});
		} else {
			circle.setCenter(center);
			circle.dot.setCenter(center);
		}
		this.update();
	};

	this.update = function() {
		if(circle == null)
			return;
		this.cleanMarkers();
		var bounds = circle.getBounds();
		var northEast = bounds.getNorthEast();
		var southWest = bounds.getSouthWest();
		var boundaries = {
			north : northEast.lat(),
			east : northEast.lng(),
			south : southWest.lat(),
			west : southWest.lng()
		};
		new Websocket().send(boundaries);
	};

	this.setRadius = function(meter) {
		if(circle == null)
			return;
		circle.setRadius(meter);
	};

	this.getGoogleMap = function() {
		return map;
	};

	this.getGoogleCircle = function() {
		return circle;
	};

	setInterval(this.cleanMarkers, 1000);
	setInterval(updateTime, 1000);

}
<|endoftext|>


<commit_before>var db = require('./db-api');
var log = require('debug')('broadcast:arg-parse');

function plusOneOf(text, marker) {
  var tokens = text.split(' ');

  var markerIdx = tokens.indexOf(marker);
  if (markerIdx > -1) {
    return tokens[markerIdx + 1];
  }

  return null;
}

exports.bannedUser = function (text) {
  return plusOneOf(text, '—ban');
}

exports.unbannedUser = function (text) {
  return plusOneOf(text, '—unban');
}

exports.adminUser = function (text) {
  return plusOneOf(text, '—admin');
}

exports.deadminUser = function (text) {
  return plusOneOf(text, '—deadmin');
}

exports.offTeam = function (text) {
  return plusOneOf(text, '—turnoff');
}

exports.onTeam = function (text) {
  return plusOneOf(text, '—turnon');
}

exports.addingTeam = function (text) {
  return plusOneOf(text, '—addteam');
}

exports.teamListRequested = function (text) {
  var tokens = text.split(' ');
  log(tokens);
  var teamsIdx = tokens.indexOf('—teams');
  log(teamsIdx);
  if (teamsIdx > -1) {
    if (tokens.length < 3) return true;
    if (tokens[teamsIdx + 1][0] != '[') return true;
  }

  return null;
}

exports.teamList = function (askingTeamDomain, fn) {
  db.allTeams(function (err, teams) {
    if (err) return fn(err);

    var askingTeam = teams.filter(t => t.domain == askingTeamDomain)[0];

    var teamsDisplay = '';
    teams.forEach(t => {
      teamsDisplay += `• ${t.domain} -->> ${t.display}`;

      if (askingTeam.accepts.indexOf(t.id) < 0) {
        teamsDisplay += ' (muted)';
      }

      teamsDisplay += '\n';
    });

    return fn(null, `${'```'}${teamsDisplay}${'```'}`);
  });
}

exports.parseTargetTeams = function (text) {
  var tokens = text.split(' ');

  var teamsIdx = tokens.indexOf('—teams');
  if (teamsIdx > -1) {
    var startIdx = text.indexOf('[');
    var endIdx = text.indexOf(']');

    var targetTeams = text.splice(startIdx, endIdx - startIdx).split(',');
    targetTeams.forEach(t => {
      t.replace(/ /g,'');
    });
    return targetTeams;
  }

  return false;
}

exports.helpRequested = function (text) {
  var parsed = text.split(' ');
  if (parsed.length == 1) return true;
  if (parsed[1] == 'help') return true;
  if (parsed[1] == '—help') return true;
  return false;
}

exports.helpMessage = function (fn) {
  db.allTeams(function (err, teams) {
    if (err) return fn(err);

    var teamsDisplay = '';
    teams.forEach(t => {
      teamsDisplay += `• ${t.domain} -->> ${t.display}\n`;
    });

    return fn(null, `Welcome to broadcast! A Slack bot that sends messages across Slack teams.
*Broadcast a message* with:
${'```'}broadcast: <your message here>${'```'}

*Target your message to specific teams* by using \`--teams\`:
${'```'}broadcast: --teams [domain name 1, domain name 2, ...] <your message here>${'```'}
_Note – anything in between [ and ] will be interpreted as a team domain_

To *quiet a team*, type:
${'```'}broadcast: —turnoff <team domain>${'```'}

To *"unquiet" a team*, type:
${'```'}broadcast: --turnon <team domain>${'```'}

To see this *help message*, type:
${'```'}broadcast: help${'```'}

To see the *list of connected teams*, type:
${'```'}broadcast: --teams${'```'} with nothing after.

You currently have ${teams.length} teams connected to your broadcast:
${'```'}
${teamsDisplay}
${'```'}
Contact ben.paul.ryan.packer@gmail.com with questions`);
  });
}

<commit_msg>always accept multiple dash types<commit_after>var db = require('./db-api');
var log = require('debug')('broadcast:arg-parse');

function indexOfMarker(tokens, marker) {
  var possibilities = [
    tokens.indexOf(`—${marker}`),
    tokens.indexOf(`-${marker}`),
    tokens.indexOf(`--${marker}`),
    tokens.indexOf(`––${marker}`)
  ];
  possibilities.forEach(idx => {
    if (idx > -1) return idx;
  });
  return -1;
}

function plusOneOf(text, marker) {
  var tokens = text.split(' ');

  var markerIdx = indexOfMarker(tokens, marker);
  if (markerIdx > -1) {
    return tokens[markerIdx + 1];
  }

  return null;
}

exports.bannedUser = function (text) {
  return plusOneOf(text, 'ban');
}

exports.unbannedUser = function (text) {
  return plusOneOf(text, 'unban');
}

exports.adminUser = function (text) {
  return plusOneOf(text, 'admin');
}

exports.deadminUser = function (text) {
  return plusOneOf(text, 'deadmin');
}

exports.offTeam = function (text) {
  return plusOneOf(text, 'turnoff');
}

exports.onTeam = function (text) {
  return plusOneOf(text, 'turnon');
}

exports.addingTeam = function (text) {
  return plusOneOf(text, 'addteam');
}

exports.teamListRequested = function (text) {
  var tokens = text.split(' ');
  log(tokens);
  var teamsIdx = indexOfMarker(tokens, 'teams');
  log(teamsIdx);
  if (teamsIdx > -1) {
    if (tokens.length < 3) return true;
    if (tokens[teamsIdx + 1][0] != '[') return true;
  }

  return null;
}

exports.teamList = function (askingTeamDomain, fn) {
  db.allTeams(function (err, teams) {
    if (err) return fn(err);

    var askingTeam = teams.filter(t => t.domain == askingTeamDomain)[0];

    var teamsDisplay = '';
    teams.forEach(t => {
      teamsDisplay += `• ${t.domain} -->> ${t.display}`;

      if (askingTeam.accepts.indexOf(t.id) < 0) {
        teamsDisplay += ' (muted)';
      }

      teamsDisplay += '\n';
    });

    return fn(null, `${'```'}${teamsDisplay}${'```'}`);
  });
}

exports.parseTargetTeams = function (text) {
  var tokens = text.split(' ');

  var teamsIdx = indexOfMarker(tokens, 'teams');
  if (teamsIdx > -1) {
    var startIdx = text.indexOf('[');
    var endIdx = text.indexOf(']');

    var targetTeams = text.splice(startIdx, endIdx - startIdx).split(',');
    targetTeams.forEach(t => {
      t.replace(/ /g,'');
    });
    return targetTeams;
  }

  return false;
}

exports.helpRequested = function (text) {
  var parsed = text.split(' ');
  if (parsed.length == 1) return true;
  if (indexOfMarker(tokens, 'help') > -1) return true;
  return false;
}

exports.helpMessage = function (fn) {
  db.allTeams(function (err, teams) {
    if (err) return fn(err);

    var teamsDisplay = '';
    teams.forEach(t => {
      teamsDisplay += `• ${t.domain} -->> ${t.display}\n`;
    });

    return fn(null, `Welcome to broadcast! A Slack bot that sends messages across Slack teams.
*Broadcast a message* with:
${'```'}broadcast: <your message here>${'```'}

*Target your message to specific teams* by using \`--teams\`:
${'```'}broadcast: --teams [domain name 1, domain name 2, ...] <your message here>${'```'}
_Note – anything in between [ and ] will be interpreted as a team domain_

To *quiet a team*, type:
${'```'}broadcast: —turnoff <team domain>${'```'}

To *"unquiet" a team*, type:
${'```'}broadcast: --turnon <team domain>${'```'}

To see this *help message*, type:
${'```'}broadcast: help${'```'}

To see the *list of connected teams*, type:
${'```'}broadcast: --teams${'```'} with nothing after.

You currently have ${teams.length} teams connected to your broadcast:
${'```'}
${teamsDisplay}
${'```'}
Contact ben.paul.ryan.packer@gmail.com with questions`);
  });
}

<|endoftext|>


<commit_before>var appConfig = require('./ingest-config'),
    request = require('request');

var ingestPlugin = {};

var db = undefined;

var init = function(app, dbConn) {
  db = dbConn;
};
ingestPlugin.init = init;

var ingest = function(meta,data, done) {
  data._meta = {
    'user' : meta.user,
    'api' : meta.api,
    'url' : meta.url,
    'createTime' : new Date().getTime()
  };

  db.collection('temporary').save(data, function(err,doc){
     if(err) {
       console.log('Failed to save temp data: ' + JSON.stringify(err));
       done(undefined);
     } else {
       if(Array.isArray(doc)) {
         var docs = []; // should use _ here
         for(var i=0;i<doc.length;i++) {
           docs.push(doc[i]._id);
         }
         done(docs);
       }
       else {
         done([doc._id]);
       }
     }
  });
};
ingestPlugin.ingest = ingest;

var query = function(meta,query,done) {

  if(!meta || !meta.user || meta.user.trim().length==0) {
     console.log('Queries must have user');
     done(undefined);
     return;
  }

  var data = {};

  // inspect and translate the params for query
  try {
    for(var key in query) {
      var value = query[key];
      var operator = 'eq'; // equal is the default operator

      // key~op indicates which operator should be used.
      if(key.match(/^.+\~(eq|lt|lte|gt|ne|in|nin|re|rei)$/i)) { // NOTE: case insensitive here
        var delimPosition = key.lastIndexOf('~');
        operator = key.substring(delimPosition+1).toLowerCase();
        key = key.substring(0,delimPosition);
      }

      // make sure the data is clean
      if(!(typeof key === "string"            // key is string
         && typeof value  === "string"      // value is string
         && key.trim().length > 0           // key must not be empty
         && key.match(/^[a-zA-Z0-9._-]+$/)   // key only alpha, numeric, dot, underscore, dash
         && value.trim().length > 0)) {      // value must not be empty
        throw ('invalid parameter: ' + key + ' = ' + value);

      }

      // cast values. take into account that value may need to be split
      var castValues = operator.match(/^(in|nin)$/) ? value.split('~') : [value];
      for(var v=0;v<castValues.length;v++) {
        var castValue = castValues[v];

        // handle casting
        if(castValue.match(/^[+-]?[0-9]+$/)) {
          castValue = parseInt(castValue);
        }
        else if(castValue.match(/^[+-]?[0-9]+\.[0-9]+$/)) {
          castValue = parseFloat(castValue);
        }

        castValues[v] = castValue;
      }
      value = castValues.length==1 ? castValues[0] : castValues;

      if(operator === "eq") {
        data[key] = value;
      }
      else if(operator === "re" || operator === "rei") {
        try {
          var regex;
          if(operator === "rei") {
            regex = new RegExp(value,"i");
          }
          else {
            regex = new RegExp(value);
          }
          data[key] = regex;
        }
        catch(e) {
          throw ('invalid regex: ' + value);
        }
      }
      else {
        var conditionKey = '$' + operator;
        var conditionValue;
        // account for array values for IN and NOT IN
        if(operator.match(/^(in|nin)$/)) {
          if(Array.isArray(value)) {
            conditionValue = value;
          }
          else { // it needs to be an array
            conditionValue = [value];
          }
        }
        else {
          conditionValue = value;
        }

        //account for multiple conditions
        // only doing AND conditions right now
        // in and not in exist for OR conditions
        if(data[key]) {
          // only use condition if an equal or regex condition not present
          // equal conditions will be string values
          if(typeof(data[key]) === "string"
             || data[key] instanceof Regexp) {
            throw  'not handling compound conditions for eq, re, or rei operators';
          }
          else {
            data[key][conditionKey] = conditionValue;
          }
        }
        else {
          data[key] = {};
          data[key][conditionKey] = conditionValue;
        }
      }
    }
    console.log("QUERY = " + JSON.stringify(data));
  }
  catch(ex) {
    console.log('INVALID QUERY: ' + ex);
    done(undefined);
    return;
  }

// this must be last
data['_meta.user'] = meta.user;

var options = {
  "limit": 20
};

db.collection('temporary').find(data,options).toArray(function(err,docs){
   if(err) {
     console.log('Failed to find matching data: ' + JSON.stringify(err));
     done(undefined);
   }
   else {
     done(docs);
   }
});

};
ingestPlugin.query = query;

module.exports = ingestPlugin;
<commit_msg>improved logic around detecting non-supported conditions<commit_after>var appConfig = require('./ingest-config'),
    request = require('request');

var ingestPlugin = {};

var db = undefined;

var init = function(app, dbConn) {
  db = dbConn;
};
ingestPlugin.init = init;

var ingest = function(meta,data, done) {
  data._meta = {
    'user' : meta.user,
    'api' : meta.api,
    'url' : meta.url,
    'createTime' : new Date().getTime()
  };

  db.collection('temporary').save(data, function(err,doc){
     if(err) {
       console.log('Failed to save temp data: ' + JSON.stringify(err));
       done(undefined);
     } else {
       if(Array.isArray(doc)) {
         var docs = []; // should use _ here
         for(var i=0;i<doc.length;i++) {
           docs.push(doc[i]._id);
         }
         done(docs);
       }
       else {
         done([doc._id]);
       }
     }
  });
};
ingestPlugin.ingest = ingest;

var query = function(meta,query,done) {

  if(!meta || !meta.user || meta.user.trim().length==0) {
     console.log('Queries must have user');
     done(undefined);
     return;
  }

  var data = {};

  // inspect and translate the params for query
  try {
    for(var key in query) {
      var value = query[key];
      var operator = 'eq'; // equal is the default operator

      // key~op indicates which operator should be used.
      if(key.match(/^.+\~(eq|lt|lte|gt|ne|in|nin|re|rei)$/i)) { // NOTE: case insensitive here
        var delimPosition = key.lastIndexOf('~');
        operator = key.substring(delimPosition+1).toLowerCase();
        key = key.substring(0,delimPosition);
      }

      // make sure the data is clean
      if(!(typeof key === "string"            // key is string
         && typeof value  === "string"      // value is string
         && key.trim().length > 0           // key must not be empty
         && key.match(/^[a-zA-Z0-9._-]+$/)   // key only alpha, numeric, dot, underscore, dash
         && value.trim().length > 0)) {      // value must not be empty

        throw ('invalid parameter: ' + key + ' = ' + value);
      }

      // error if similar condition already defined or not compatible
      if(data[key] &&
         (operator.match(/^(eq|re|rei)$/)
          || (typeof(data[key]) === "string"
          || data[key] instanceof Regexp)) {

          throw  'not handling compound conditions for eq, re, or rei operators';
      }

      // cast values. take into account that value may need to be split
      var castValues = operator.match(/^(in|nin)$/) ? value.split('~') : [value];
      for(var v=0;v<castValues.length;v++) {
        var castValue = castValues[v];

        // handle casting
        if(castValue.match(/^[+-]?[0-9]+$/)) {
          castValue = parseInt(castValue);
        }
        else if(castValue.match(/^[+-]?[0-9]+\.[0-9]+$/)) {
          castValue = parseFloat(castValue);
        }

        castValues[v] = castValue;
      }
      value = castValues.length==1 ? castValues[0] : castValues;

      if(operator === "eq") {
        data[key] = value;
      }
      else if(operator === "re" || operator === "rei") {
        try {
          var regex;
          if(operator === "rei") {
            regex = new RegExp(value,"i");
          }
          else {
            regex = new RegExp(value);
          }
          data[key] = regex;
        }
        catch(e) {
          throw ('invalid regex: ' + value);
        }
      }
      else {
        var conditionKey = '$' + operator;
        var conditionValue;
        // account for array values for IN and NOT IN
        if(operator.match(/^(in|nin)$/)) {
          if(Array.isArray(value)) {
            conditionValue = value;
          }
          else { // it needs to be an array
            conditionValue = [value];
          }
        }
        else {
          conditionValue = value;
        }

        //account for multiple conditions
        if(!data[key]) {
          data[key] = {};
        }
        data[key][conditionKey] = conditionValue;
      }
    }
    console.log("QUERY = " + JSON.stringify(data));
  }
  catch(ex) {
    console.log('INVALID QUERY: ' + ex);
    done(undefined);
    return;
  }

// this must be last
data['_meta.user'] = meta.user;

var options = {
  "limit": 20
};

db.collection('temporary').find(data,options).toArray(function(err,docs){
   if(err) {
     console.log('Failed to find matching data: ' + JSON.stringify(err));
     done(undefined);
   }
   else {
     done(docs);
   }
});

};
ingestPlugin.query = query;

module.exports = ingestPlugin;
<|endoftext|>


<commit_before>"use strict";
var flag;

(function () {

    window.addEventListener("storage", function (e) {
        if (e.key) {
            e.key == "session@cloud" && e.newValue && chrome.runtime.sendMessage({"id": "oa", "oa": JSON.parse(e.newValue).feedlyToken});
        } else {
            chrome.runtime.sendMessage({"id": "clr"});
        }
    });

    var tmp = document.hasOwnProperty("hidden") ? ["hidden", ""] : ["webkitHidden", "webkit"];
    flag = tmp[0];
    tmp = tmp[1];

    document.addEventListener(tmp + "visibilitychange", function () {
        if (document[flag]) {
            chrome.runtime.sendMessage({"id": "off", "un": parseInt(document.querySelector("#latesttab_header div").innerHTML) || 0});
        } else {
            chrome.runtime.sendMessage({"id": "on"})
        }
    });
    window.addEventListener("beforeunload", function () {
        chrome.runtime.sendMessage({"id": "off"})
    });

    setTimeout(function(){document[flag] || chrome.runtime.sendMessage({"id": "on"})},3000);
})();<commit_msg>Update inject.js<commit_after>"use strict";
var flag;

(function () {

    window.addEventListener("storage", function (e) {
        if (e.key) {
            if (e.key == "feedly.session" && e.newValue) {
                e = JSON.parse(e.newValue);
                chrome.runtime.sendMessage({"id": "oa", "oa": e.feedlyToken, "i": e.id})
            }
        } else {
            chrome.runtime.sendMessage({"id": "clr"});
        }
    });

    var tmp = document.hasOwnProperty("hidden") ? ["hidden", ""] : ["webkitHidden", "webkit"];
    flag = tmp[0];
    tmp = tmp[1];

    document.addEventListener(tmp + "visibilitychange", function () {
        if (document[flag]) {
            var d=document.querySelector("#latesttab_header div");
            chrome.runtime.sendMessage({"id": "off", "un": d?parseInt(d.innerHTML) || 0:0});
        } else {
            chrome.runtime.sendMessage({"id": "on"})
        }
    });
    window.addEventListener("beforeunload", function () {
        chrome.runtime.sendMessage({"id": "off"})
    });

    setTimeout(function () {
        document[flag] || chrome.runtime.sendMessage({"id": "on"})
    }, 3000);
})();
<|endoftext|>


<commit_before>/*global logger*/
/*
    WidgetName
    ========================

    @file      : WidgetName.js
    @version   : {{version}}
    @author    : {{author}}
    @date      : {{date}}
    @copyright : {{copyright}}
    @license   : {{license}}

    Documentation
    ========================
    Describe your widget here.
*/

// Required module list. Remove unnecessary modules, you can always get them back from the boilerplate.
define([
    "dojo/_base/declare",
    "mxui/widget/_WidgetBase",
    "dijit/_TemplatedMixin",

    "mxui/dom",
    "dojo/dom",
    "dojo/dom-prop",
    "dojo/dom-geometry",
    "dojo/dom-class",
    "dojo/dom-style",
    "dojo/dom-construct",
    "dojo/_base/array",
    "dojo/_base/lang",
    "dojo/text",
    "dojo/html",
    "dojo/_base/event",

    "WidgetName/lib/jquery-1.11.2",
    "dojo/text!WidgetName/widget/template/WidgetName.html"
], function (declare, _WidgetBase, _TemplatedMixin, dom, dojoDom, dojoProp, dojoGeometry, dojoClass, dojoStyle, dojoConstruct, dojoArray, dojoLang, dojoText, dojoHtml, dojoEvent, _jQuery, widgetTemplate) {
    "use strict";

    var $ = _jQuery.noConflict(true);

    // Declare widget's prototype.
    return declare("WidgetName.widget.WidgetName", [ _WidgetBase, _TemplatedMixin ], {
        // _TemplatedMixin will create our dom node using this HTML template.
        templateString: widgetTemplate,

        // DOM elements
        inputNodes: null,
        colorSelectNode: null,
        colorInputNode: null,
        infoTextNode: null,

        // Parameters configured in the Modeler.
        mfToExecute: "",
        messageString: "",
        backgroundColor: "",

        // Internal variables. Non-primitives created in the prototype are shared between all widget instances.
        _handles: null,
        _contextObj: null,
        _alertDiv: null,
        _readOnly: false,

        // dojo.declare.constructor is called to construct the widget instance. Implement to initialize non-primitive properties.
        constructor: function () {
            logger.debug(this.id + ".constructor");
            this._handles = [];
        },

        // dijit._WidgetBase.postCreate is called after constructing the widget. Implement to do extra setup work.
        postCreate: function () {
            logger.debug(this.id + ".postCreate");

            if (this.readOnly || this.get("disabled") || this.readonly) {
              this._readOnly = true;
            }

            this._updateRendering();
            this._setupEvents();
        },

        // mxui.widget._WidgetBase.update is called when context is changed or initialized. Implement to re-render and / or fetch data.
        update: function (obj, callback) {
            logger.debug(this.id + ".update");

            this._contextObj = obj;
            this._resetSubscriptions();
            this._updateRendering(callback); // We're passing the callback to updateRendering to be called after DOM-manipulation
        },

        // mxui.widget._WidgetBase.enable is called when the widget should enable editing. Implement to enable editing if widget is input widget.
        enable: function () {
          logger.debug(this.id + ".enable");
        },

        // mxui.widget._WidgetBase.enable is called when the widget should disable editing. Implement to disable editing if widget is input widget.
        disable: function () {
          logger.debug(this.id + ".disable");
        },

        // mxui.widget._WidgetBase.resize is called when the page's layout is recalculated. Implement to do sizing calculations. Prefer using CSS instead.
        resize: function (box) {
          logger.debug(this.id + ".resize");
        },

        // mxui.widget._WidgetBase.uninitialize is called when the widget is destroyed. Implement to do special tear-down work.
        uninitialize: function () {
          logger.debug(this.id + ".uninitialize");
            // Clean up listeners, helper objects, etc. There is no need to remove listeners added with this.connect / this.subscribe / this.own.
            this._unsubscribe();
        },

        // We want to stop events on a mobile device
        _stopBubblingEventOnMobile: function (e) {
            logger.debug(this.id + "._stopBubblingEventOnMobile");
            if (typeof document.ontouchstart !== "undefined") {
                dojoEvent.stop(e);
            }
        },

        // Attach events to HTML dom elements
        _setupEvents: function () {
            logger.debug(this.id + "._setupEvents");
            this.connect(this.colorSelectNode, "change", function (e) {
                // Function from mendix object to set an attribute.
                this._contextObj.set(this.backgroundColor, this.colorSelectNode.value);
            });

            this.connect(this.infoTextNode, "click", function (e) {
                // Only on mobile stop event bubbling!
                this._stopBubblingEventOnMobile(e);

                // If a microflow has been set execute the microflow on a click.
                if (this.mfToExecute !== "") {
                    mx.data.action({
                        params: {
                            applyto: "selection",
                            actionname: this.mfToExecute,
                            guids: [ this._contextObj.getGuid() ]
                        },
                        store: {
                            caller: this.mxform
                        },
                        callback: function (obj) {
                            //TODO what to do when all is ok!
                        },
                        error: dojoLang.hitch(this, function (error) {
                            logger.error(this.id + ": An error occurred while executing microflow: " + error.description);
                        })
                    }, this);
                }
            });
        },

        // Rerender the interface.
        _updateRendering: function (callback) {
            logger.debug(this.id + "._updateRendering");
            this.colorSelectNode.disabled = this._readOnly;
            this.colorInputNode.disabled = this._readOnly;

            if (this._contextObj !== null) {
                dojoStyle.set(this.domNode, "display", "block");

                var colorValue = this._contextObj.get(this.backgroundColor);

                this.colorInputNode.value = colorValue;
                this.colorSelectNode.value = colorValue;

                dojoHtml.set(this.infoTextNode, this.messageString);
                dojoStyle.set(this.infoTextNode, "background-color", colorValue);
            } else {
                dojoStyle.set(this.domNode, "display", "none");
            }

            // Important to clear all validations!
            this._clearValidations();

            // The callback, coming from update, needs to be executed, to let the page know it finished rendering
            this._executeCallback(callback);
        },

        // Handle validations.
        _handleValidation: function (validations) {
            logger.debug(this.id + "._handleValidation");
            this._clearValidations();

            var validation = validations[0],
                message = validation.getReasonByAttribute(this.backgroundColor);

            if (this._readOnly) {
                validation.removeAttribute(this.backgroundColor);
            } else if (message) {
                this._addValidation(message);
                validation.removeAttribute(this.backgroundColor);
            }
        },

        // Clear validations.
        _clearValidations: function () {
            logger.debug(this.id + "._clearValidations");
            dojoConstruct.destroy(this._alertDiv);
            this._alertDiv = null;
        },

        // Show an error message.
        _showError: function (message) {
            logger.debug(this.id + "._showError");
            if (this._alertDiv !== null) {
                dojoHtml.set(this._alertDiv, message);
                return true;
            }
            this._alertDiv = dojoConstruct.create("div", {
                "class": "alert alert-danger",
                "innerHTML": message
            });
            dojoConstruct.place(this._alertDiv, this.domNode);
        },

        // Add a validation.
        _addValidation: function (message) {
            logger.debug(this.id + "._addValidation");
            this._showError(message);
        },

        _unsubscribe: function () {
          if (this._handles) {
              dojoArray.forEach(this._handles, function (handle) {
                  this.unsubscribe(handle);
              });
              this._handles = [];
          }
        },

        // Reset subscriptions.
        _resetSubscriptions: function () {
            logger.debug(this.id + "._resetSubscriptions");
            // Release handles on previous object, if any.
            this._unsubscribe();

            // When a mendix object exists create subscribtions.
            if (this._contextObj) {
                var objectHandle = this.subscribe({
                    guid: this._contextObj.getGuid(),
                    callback: dojoLang.hitch(this, function (guid) {
                        this._updateRendering();
                    })
                });

                var attrHandle = this.subscribe({
                    guid: this._contextObj.getGuid(),
                    attr: this.backgroundColor,
                    callback: dojoLang.hitch(this, function (guid, attr, attrValue) {
                        this._updateRendering();
                    })
                });

                var validationHandle = this.subscribe({
                    guid: this._contextObj.getGuid(),
                    val: true,
                    callback: dojoLang.hitch(this, this._handleValidation)
                });

                this._handles = [ objectHandle, attrHandle, validationHandle ];
            }
        },

        _executeCallback: function (cb) {
          if (cb && typeof cb === "function") {
            cb();
          }
        }
    });
});

require(["WidgetName/widget/WidgetName"]);
<commit_msg>Update WidgetName.js<commit_after>/*global logger*/
/*
    WidgetName
    ========================

    @file      : WidgetName.js
    @version   : {{version}}
    @author    : {{author}}
    @date      : {{date}}
    @copyright : {{copyright}}
    @license   : {{license}}

    Documentation
    ========================
    Describe your widget here.
*/

// Required module list. Remove unnecessary modules, you can always get them back from the boilerplate.
define([
    "dojo/_base/declare",
    "mxui/widget/_WidgetBase",
    "dijit/_TemplatedMixin",

    "mxui/dom",
    "dojo/dom",
    "dojo/dom-prop",
    "dojo/dom-geometry",
    "dojo/dom-class",
    "dojo/dom-style",
    "dojo/dom-construct",
    "dojo/_base/array",
    "dojo/_base/lang",
    "dojo/text",
    "dojo/html",
    "dojo/_base/event",

    "WidgetName/lib/jquery-1.11.2",
    "dojo/text!WidgetName/widget/template/WidgetName.html"
], function (declare, _WidgetBase, _TemplatedMixin, dom, dojoDom, dojoProp, dojoGeometry, dojoClass, dojoStyle, dojoConstruct, dojoArray, dojoLang, dojoText, dojoHtml, dojoEvent, _jQuery, widgetTemplate) {
    "use strict";

    var $ = _jQuery.noConflict(true);

    // Declare widget's prototype.
    return declare("WidgetName.widget.WidgetName", [ _WidgetBase, _TemplatedMixin ], {
        // _TemplatedMixin will create our dom node using this HTML template.
        templateString: widgetTemplate,

        // DOM elements
        inputNodes: null,
        colorSelectNode: null,
        colorInputNode: null,
        infoTextNode: null,

        // Parameters configured in the Modeler.
        mfToExecute: "",
        messageString: "",
        backgroundColor: "",

        // Internal variables. Non-primitives created in the prototype are shared between all widget instances.
        _handles: null,
        _contextObj: null,
        _alertDiv: null,
        _readOnly: false,

        // dojo.declare.constructor is called to construct the widget instance. Implement to initialize non-primitive properties.
        constructor: function () {
            logger.debug(this.id + ".constructor");
            this._handles = [];
        },

        // dijit._WidgetBase.postCreate is called after constructing the widget. Implement to do extra setup work.
        postCreate: function () {
            logger.debug(this.id + ".postCreate");

            if (this.readOnly || this.get("disabled") || this.readonly) {
              this._readOnly = true;
            }

            this._updateRendering();
            this._setupEvents();
        },

        // mxui.widget._WidgetBase.update is called when context is changed or initialized. Implement to re-render and / or fetch data.
        update: function (obj, callback) {
            logger.debug(this.id + ".update");

            this._contextObj = obj;
            this._resetSubscriptions();
            this._updateRendering(callback); // We're passing the callback to updateRendering to be called after DOM-manipulation
        },

        // mxui.widget._WidgetBase.enable is called when the widget should enable editing. Implement to enable editing if widget is input widget.
        enable: function () {
          logger.debug(this.id + ".enable");
        },

        // mxui.widget._WidgetBase.enable is called when the widget should disable editing. Implement to disable editing if widget is input widget.
        disable: function () {
          logger.debug(this.id + ".disable");
        },

        // mxui.widget._WidgetBase.resize is called when the page's layout is recalculated. Implement to do sizing calculations. Prefer using CSS instead.
        resize: function (box) {
          logger.debug(this.id + ".resize");
        },

        // mxui.widget._WidgetBase.uninitialize is called when the widget is destroyed. Implement to do special tear-down work.
        uninitialize: function () {
          logger.debug(this.id + ".uninitialize");
            // Clean up listeners, helper objects, etc. There is no need to remove listeners added with this.connect / this.subscribe / this.own.
        },

        // We want to stop events on a mobile device
        _stopBubblingEventOnMobile: function (e) {
            logger.debug(this.id + "._stopBubblingEventOnMobile");
            if (typeof document.ontouchstart !== "undefined") {
                dojoEvent.stop(e);
            }
        },

        // Attach events to HTML dom elements
        _setupEvents: function () {
            logger.debug(this.id + "._setupEvents");
            this.connect(this.colorSelectNode, "change", function (e) {
                // Function from mendix object to set an attribute.
                this._contextObj.set(this.backgroundColor, this.colorSelectNode.value);
            });

            this.connect(this.infoTextNode, "click", function (e) {
                // Only on mobile stop event bubbling!
                this._stopBubblingEventOnMobile(e);

                // If a microflow has been set execute the microflow on a click.
                if (this.mfToExecute !== "") {
                    mx.data.action({
                        params: {
                            applyto: "selection",
                            actionname: this.mfToExecute,
                            guids: [ this._contextObj.getGuid() ]
                        },
                        store: {
                            caller: this.mxform
                        },
                        callback: function (obj) {
                            //TODO what to do when all is ok!
                        },
                        error: dojoLang.hitch(this, function (error) {
                            logger.error(this.id + ": An error occurred while executing microflow: " + error.description);
                        })
                    }, this);
                }
            });
        },

        // Rerender the interface.
        _updateRendering: function (callback) {
            logger.debug(this.id + "._updateRendering");
            this.colorSelectNode.disabled = this._readOnly;
            this.colorInputNode.disabled = this._readOnly;

            if (this._contextObj !== null) {
                dojoStyle.set(this.domNode, "display", "block");

                var colorValue = this._contextObj.get(this.backgroundColor);

                this.colorInputNode.value = colorValue;
                this.colorSelectNode.value = colorValue;

                dojoHtml.set(this.infoTextNode, this.messageString);
                dojoStyle.set(this.infoTextNode, "background-color", colorValue);
            } else {
                dojoStyle.set(this.domNode, "display", "none");
            }

            // Important to clear all validations!
            this._clearValidations();

            // The callback, coming from update, needs to be executed, to let the page know it finished rendering
            this._executeCallback(callback);
        },

        // Handle validations.
        _handleValidation: function (validations) {
            logger.debug(this.id + "._handleValidation");
            this._clearValidations();

            var validation = validations[0],
                message = validation.getReasonByAttribute(this.backgroundColor);

            if (this._readOnly) {
                validation.removeAttribute(this.backgroundColor);
            } else if (message) {
                this._addValidation(message);
                validation.removeAttribute(this.backgroundColor);
            }
        },

        // Clear validations.
        _clearValidations: function () {
            logger.debug(this.id + "._clearValidations");
            dojoConstruct.destroy(this._alertDiv);
            this._alertDiv = null;
        },

        // Show an error message.
        _showError: function (message) {
            logger.debug(this.id + "._showError");
            if (this._alertDiv !== null) {
                dojoHtml.set(this._alertDiv, message);
                return true;
            }
            this._alertDiv = dojoConstruct.create("div", {
                "class": "alert alert-danger",
                "innerHTML": message
            });
            dojoConstruct.place(this._alertDiv, this.domNode);
        },

        // Add a validation.
        _addValidation: function (message) {
            logger.debug(this.id + "._addValidation");
            this._showError(message);
        },

        _unsubscribe: function () {
          if (this._handles) {
              dojoArray.forEach(this._handles, function (handle) {
                  this.unsubscribe(handle);
              });
              this._handles = [];
          }
        },

        // Reset subscriptions.
        _resetSubscriptions: function () {
            logger.debug(this.id + "._resetSubscriptions");
            // Release handles on previous object, if any.
            this._unsubscribe();

            // When a mendix object exists create subscribtions.
            if (this._contextObj) {
                var objectHandle = this.subscribe({
                    guid: this._contextObj.getGuid(),
                    callback: dojoLang.hitch(this, function (guid) {
                        this._updateRendering();
                    })
                });

                var attrHandle = this.subscribe({
                    guid: this._contextObj.getGuid(),
                    attr: this.backgroundColor,
                    callback: dojoLang.hitch(this, function (guid, attr, attrValue) {
                        this._updateRendering();
                    })
                });

                var validationHandle = this.subscribe({
                    guid: this._contextObj.getGuid(),
                    val: true,
                    callback: dojoLang.hitch(this, this._handleValidation)
                });

                this._handles = [ objectHandle, attrHandle, validationHandle ];
            }
        },

        _executeCallback: function (cb) {
          if (cb && typeof cb === "function") {
            cb();
          }
        }
    });
});

require(["WidgetName/widget/WidgetName"]);
<|endoftext|>