<commit_before>(function (Q, $) {

/**
 * @module Streams-tools
 */

/**
 * Displays participants of a given stream in a horizontal list.
 * Each item in the list is presented with an avatar and also can have a contextual associated with it.
 * @class Streams participants
 * @constructor
 * @param {Object} options Provide options for this tool
 *   @param {String} options.publisherId The id of the publisher
 *   @param {String} options.streamName The name of the stream
 *   @param {Object} [options.invite] Pass an object here to pass as fields to
 *     Streams/participants/invite template, otherwise the invite button doesn't appear.
 *   @param {Boolean} [options.showSummary] Whether to show a summary
 *   @param {Array} [options.ordering] Array of user id's to order avatars in participants list.
 *   Only users mentioned in this array will ordered. Other users order none change.
 *   @param {Boolean} [options.showBlanks] Whether to show blank avatars in place of remaining spots
 *   @param {Number} [options.max]
 *    The number, if any, to show in the denominator of the summary
 *   @param {Number} [options.maxShow=10]
 *    The maximum number of participants to fetch for display
 *   @param {Function} [options.filter]
 *    Takes (userId, element) and can modify them.
 *    If this function returns false, the element is not appended.
 *   @param {Function} [options.hideIfNoParticipants=false] If true hide tool element when no participants and show when somebody join
 *   @param {Q.Event} [options.onRefresh] An event that occurs when the tool is refreshed
 */
Q.Tool.define("Streams/participants",

function _Streams_participants(options) {

	var tool = this;
	var state = tool.state;
	var $toolElement = $(tool.element);

	if (!state.publisherId) {
		throw new Q.Error("Streams/chat: missing publisherId option");
	}
	if (!state.streamName) {
		throw new Q.Error("Streams/chat: missing streamName option");
	}

	tool.Q.onStateChanged('count').set(function (name) {
		var c = state.count;
		tool.$count.text(c >= 100 ? '99+' : c.toString());
		if (state.showSummary) {
			tool.$summary.show().plugin('Q/textfill', 'refresh');
		} else {
			tool.$summary.hide();
		}

		if (state.hideIfNoParticipants) {
			if (c <= 0) {
				tool.cssDisplay = $toolElement.css("display");
				$toolElement.css("display", "none");
			} else {
				$toolElement.css("display", tool.cssDisplay);
			}
		}
	}, tool);

	tool.Q.onStateChanged('ordering').set(this.orderAvatars.bind(this));

	tool.refresh(function () {
		tool.forEachChild('Users/avatar', function () {
			tool.$elements[this.state.userId] = $(this.element);
		});
	});
},

{
	invite: {
		userChooser: true,
		appUrl: function () {
			return location.href;
		}
	},
	ordering: [],
	hideIfNoParticipants: false,
	maxShow: 10,
	maxLoad: 100,
	max: null,
	filter: function () { },
	avatar: {
		"short": true,
		icon: (window.devicePixelRatio > 1 ? '80' : '40'),
		reflectChanges: false
	},
	showSummary: true,
	showControls: false,
	showBlanks: false,
	onRefresh: new Q.Event(),
	onInvited: new Q.Event(function (err, data) {
		var msg = Q.firstErrorMessage(err, data);
		if (msg) {
			if (msg.includes("not logged")) {
				Q.Users.login();
			}
		}
	}),
	templates: {
		invite: {
			name: 'Streams/participants/invite',
			fields: {
				src: Q.Streams.iconUrl('labels/Streams/invited', 40),
				alt: 'Invite',
				title: 'Invite'
			}
		}
	}
},

{
	Q: {
		beforeRemove: function () {
			clearInterval(this.adjustInterval);
		}
	},
	/**
	 * Refresh the participants tool
	 * @method refresh
	 * @param {Function} callback pass a callback to be called after the refresh is done
	 */
	refresh: function (callback) {
		var tool = this;
		var state = tool.state;
		var $te = $(tool.element);
		tool.$elements = {};

		if (state.rendered) {
			tool.$count = $('.Streams_participants_count', $te);
			tool.$max = $('.Streams_participants_max', $te);
			tool.$summary = $('.Streams_participants_summary', $te);
			tool.$controls = $('.Streams_participants_controls', $te);
			tool.$pei = $('.Streams_participants_expand_img', $te);
			tool.$pet = $('.Streams_participants_expand_text', $te);
			tool.$pc = $('.Streams_participants_container', $te);
			tool.$avatars = $('.Streams_participants_avatars', $te);
			tool.$blanks = $('.Streams_participants_blanks', $te);
			_continue();
			return false;
		}

		tool.$count = $("");
		tool.$max = $("");
		tool.$pet = $("See All");
		tool.$pei = $('').attr({
			src: Q.url('{{Q}}/img/expand.png'),
			alt: "expand"
		});
		tool.$controls = $("
")
			.append(
				$("
")
				.append(tool.$pei, tool.$pet)
			).appendTo($te);
		tool.$summary = $("
")
			.append($('').append(tool.$count, tool.$max))
			.appendTo($te);
		tool.$pc = $("
")
			.appendTo($te);
		tool.$avatars = $("")
			.appendTo(tool.$pc);
		tool.$blanks = $("")
			.appendTo(tool.$pc);

		// set expand icon click event
		tool.$pei.plugin('Q/clickable').on(Q.Pointer.fastclick, function () {
			if (state.expanded) {
				tool.$blanks.show();
				$te.animate({height: state.originalHeight}, function () {
					state.expanded = false;
				});
				tool.$pei.attr({
					src: Q.url('{{Q}}/img/expand.png'),
					alt: 'expand'
				});
				tool.$pet.html('See All');
			} else {
				state.originalHeight = $te.height();
				tool.$blanks.hide();
				$te.animate({height: tool.$pc.height()}, function () {
					state.expanded = true;
				});
				tool.$pei.attr({
					src: Q.url('{{Q}}/img/collapse.png'),
					alt: 'collapse'
				});
				tool.$pet.html('Fewer');
			}
		});

		$te.addClass('Streams_participants_loading');
		Q.Streams.get(state.publisherId, state.streamName, function (err, stream, extra) {
			var fem = Q.firstErrorMessage(err);
			if (fem) {
				return console.warn("Streams/participants: " + fem);
			}
			if (!$(tool.element).closest('body').length) {
				return;
			}
			tool.stream = this;
			var i = 0, c = 0;
			$te.removeClass('Streams_participants_loading');
			Q.Tool.clear(tool.$avatars[0]);
			Q.Tool.clear(tool.$blanks[0]);
			tool.$avatars.empty();
			tool.$blanks.empty();
			Q.each(extra && extra.participants, function (userId, participant) {
				if (participant.state !== 'participating') {
					return;
				}
				++c;
				if (!state.maxShow || ++i <= state.maxShow) {
					_addAvatar(userId);
				}
			}, { sort: 'insertedTime', ascending: false });
			state.count = c;
			if (state.showBlanks) {
				Q.each(c, state.maxShow-1, 1, function () {
					_addAvatar('');
				});
			}
			_continue();

		}, {participants: state.maxLoad});
		return true;

		function _continue() {
			tool.stateChanged('count');

			tool.adjustInterval = setInterval(_adjustInterval, 500);

			if (state.max) {
				tool.$max.text('/' + state.max);
			}

			Q.Streams.retainWith(tool).get(state.publisherId, state.streamName, function () {
				var stream = this;
				stream.onMessage("Streams/join")
				.set(function (stream, message, messages) {
					_addAvatar(message.byUserId, true);
					++tool.state.count;
					tool.stateChanged('count');
				}, tool);
				stream.onMessage("Streams/leave")
				.set(function (stream, message, messages) {
					_removeAvatar(message.byUserId);
					--tool.state.count;
					tool.stateChanged('count');
				}, tool);
				var si = state.invite;
				if (!si || !stream.testAdminLevel('invite')) {
					Q.handle(callback, tool, []);
					return Q.handle(state.onRefresh, tool, []);
				}
				if (tool.$('.Streams_inviteTrigger').length) {
					return; // the invite button already rendered
				}
				Q.Text.get("Streams/content", function (err, result) {
					var text = result && result.invite;
					if (text) {
						state.templates.invite.fields.alt = text.command;
						state.templates.invite.fields.title = text.command;
					}
					Q.Template.render(
						'Streams/participants/invite',
						state.templates.invite.fields,
						function (err, html) {
							if (err) return;
							var $element = tool.$invite = $(html).insertBefore(tool.$avatars);
							var filter = '.Streams_inviteTrigger';
							$(tool.element).on(Q.Pointer.fastclick, filter, function () {
								var options = Q.extend({
									identifier: si.identifier
								}, si);
								Q.Streams.invite(
									state.publisherId,
									state.streamName,
									options,
									function (err, data) {
										state.onInvited.handle.call(tool, err, data);
									}
								);
								return false;
							}).on(Q.Pointer.click, filter, function () {
								return false;
							}).on(Q.Pointer.start.eventName, filter, function () {
								$(tool.element).addClass('Q_discouragePointerEvents');
								function _pointerEndHandler() {
									$(tool.element).removeClass('Q_discouragePointerEvents');
									$(window).off(Q.Pointer.end, _pointerEndHandler);
								}
								$(window).on(Q.Pointer.end, _pointerEndHandler);
							});

							if (si.clickable) {
								$('img', $element).plugin(
									'Q/clickable', Q.extend({
										triggers: $element
									}, si.clickable)
								);
							}
							Q.handle(callback, tool, []);
							Q.handle(state.onRefresh, tool, []);
						},
						state.templates.invite
					);
				});
			});
		}

		function _addAvatar(userId, prepend) {
			var $e = userId ? tool.$avatars : tool.$blanks;
			if (userId && $(".Users_avatar_tool[id*=" + userId + "]", $e).length) {
				return;
			}

			var $element = $(Q.Tool.setUpElement(
				'div',
				'Users/avatar',
				Q.extend({}, state.avatar, {
					userId: userId,
				}),
				userId || null,
				tool.prefix)
			);
			if (false === Q.handle(state.filter, tool, [userId, $element[0]])) {
				return;
			}

			$element[prepend?'prependTo':'appendTo']($e).activate();

			tool.orderAvatars();
		}

		function _removeAvatar(userId) {
			var $element = tool.$elements[userId];
			if ($element) {
				Q.removeElement($element[0], true);
			}
		}

		function _adjustInterval() {
			var w = $te.width();
			var pm = tool.$pc.outerWidth(true) - tool.$pc.width();
			if (state.showSummary) {
				w = w - tool.$summary.outerWidth(true);
			}
			if (state.showControls) {
				w = w - tool.$controls.outerWidth(true);
				var $pcw = tool.$pc.innerWidth();
				var avatarsWidth = 0;
				$(".Streams_participants_invite", tool.$pc).add(".Users_avatar_tool", tool.$avatars).each(function () {
					avatarsWidth += $(this).outerWidth(true);
				});
				var overflowed = ($pcw > 0 && avatarsWidth > $pcw);
				if (overflowed) {
					if (!state.overflowed) {
						$te.addClass('Q_overflowed');
					}
				} else {
					$te.removeClass('Q_overflowed');
					tool.$blanks.show();
				}
				state.overflowed = overflowed;
			}

			tool.$pc.width(w - pm);
		}
	},
	/**
	 * Reorder avatars according to state.orders array
	 * @method orderAvatars
	 */
	orderAvatars: function () {
		var tool = this;
		var state = this.state;
		var $avatars = $(".Users_avatar_tool", tool.$avatars);

		// if state.ordering array empty, this method have no sense
		if (Q.isEmpty(state.ordering)) {
			return;
		}

		$avatars.each(function () {
			var avatarTool = Q.Tool.from(this, "Users/avatar");
			if (!avatarTool) {
				return;
			}

			var userId = avatarTool.state.userId;
			var $element = $(this);

			if (state.ordering.includes(userId)) {
				var elementInsertAfter = null;
				var $avatars = $(".Users_avatar_tool", tool.$avatars);

				for (var i = 0; i < state.ordering.length; i++) {
					if (state.ordering[i] === userId) {
						elementInsertAfter ? $element.insertAfter(elementInsertAfter) : $element.prependTo(tool.$avatars);
						break;
					}

					$avatars.each(function () {
						if (Q.getObject("state.userId", Q.Tool.from(this, "Users/avatar")) === state.ordering[i]) {
							elementInsertAfter = this;
						}
					});
				}

				$element.addClass("Streams_participants_ordered");
			} else {
				var $lastOrdered = $(".Streams_participants_ordered", tool.$avatars).last();
				if ($lastOrdered.length) {
					$element.insertAfter($lastOrdered);
				} else {
					$element.prependTo(tool.$avatars);
				}
			}
		});
	}
});

Q.Template.set('Streams/participants/invite',
	'
' +
	'	' +
	'
{{& title}}
' +
	'
'
);

})(Q, jQuery);<commit_msg>Updated participants tool. Call orderAvatars after avatar tool activated.<commit_after>(function (Q, $) {

/**
 * @module Streams-tools
 */

/**
 * Displays participants of a given stream in a horizontal list.
 * Each item in the list is presented with an avatar and also can have a contextual associated with it.
 * @class Streams participants
 * @constructor
 * @param {Object} options Provide options for this tool
 *   @param {String} options.publisherId The id of the publisher
 *   @param {String} options.streamName The name of the stream
 *   @param {Object} [options.invite] Pass an object here to pass as fields to
 *     Streams/participants/invite template, otherwise the invite button doesn't appear.
 *   @param {Boolean} [options.showSummary] Whether to show a summary
 *   @param {Array} [options.ordering] Array of user id's to order avatars in participants list.
 *   Only users mentioned in this array will ordered. Other users order none change.
 *   @param {Boolean} [options.showBlanks] Whether to show blank avatars in place of remaining spots
 *   @param {Number} [options.max]
 *    The number, if any, to show in the denominator of the summary
 *   @param {Number} [options.maxShow=10]
 *    The maximum number of participants to fetch for display
 *   @param {Function} [options.filter]
 *    Takes (userId, element) and can modify them.
 *    If this function returns false, the element is not appended.
 *   @param {Function} [options.hideIfNoParticipants=false] If true hide tool element when no participants and show when somebody join
 *   @param {Q.Event} [options.onRefresh] An event that occurs when the tool is refreshed
 */
Q.Tool.define("Streams/participants",

function _Streams_participants(options) {

	var tool = this;
	var state = tool.state;
	var $toolElement = $(tool.element);

	if (!state.publisherId) {
		throw new Q.Error("Streams/chat: missing publisherId option");
	}
	if (!state.streamName) {
		throw new Q.Error("Streams/chat: missing streamName option");
	}

	tool.Q.onStateChanged('count').set(function (name) {
		var c = state.count;
		tool.$count.text(c >= 100 ? '99+' : c.toString());
		if (state.showSummary) {
			tool.$summary.show().plugin('Q/textfill', 'refresh');
		} else {
			tool.$summary.hide();
		}

		if (state.hideIfNoParticipants) {
			if (c <= 0) {
				tool.cssDisplay = $toolElement.css("display");
				$toolElement.css("display", "none");
			} else {
				$toolElement.css("display", tool.cssDisplay);
			}
		}
	}, tool);

	tool.Q.onStateChanged('ordering').set(this.orderAvatars.bind(this));

	tool.refresh(function () {
		tool.forEachChild('Users/avatar', function () {
			tool.$elements[this.state.userId] = $(this.element);
		});
	});
},

{
	invite: {
		userChooser: true,
		appUrl: function () {
			return location.href;
		}
	},
	ordering: [],
	hideIfNoParticipants: false,
	maxShow: 10,
	maxLoad: 100,
	max: null,
	filter: function () { },
	avatar: {
		"short": true,
		icon: (window.devicePixelRatio > 1 ? '80' : '40'),
		reflectChanges: false
	},
	showSummary: true,
	showControls: false,
	showBlanks: false,
	onRefresh: new Q.Event(),
	onInvited: new Q.Event(function (err, data) {
		var msg = Q.firstErrorMessage(err, data);
		if (msg) {
			if (msg.includes("not logged")) {
				Q.Users.login();
			}
		}
	}),
	templates: {
		invite: {
			name: 'Streams/participants/invite',
			fields: {
				src: Q.Streams.iconUrl('labels/Streams/invited', 40),
				alt: 'Invite',
				title: 'Invite'
			}
		}
	}
},

{
	Q: {
		beforeRemove: function () {
			clearInterval(this.adjustInterval);
		}
	},
	/**
	 * Refresh the participants tool
	 * @method refresh
	 * @param {Function} callback pass a callback to be called after the refresh is done
	 */
	refresh: function (callback) {
		var tool = this;
		var state = tool.state;
		var $te = $(tool.element);
		tool.$elements = {};

		if (state.rendered) {
			tool.$count = $('.Streams_participants_count', $te);
			tool.$max = $('.Streams_participants_max', $te);
			tool.$summary = $('.Streams_participants_summary', $te);
			tool.$controls = $('.Streams_participants_controls', $te);
			tool.$pei = $('.Streams_participants_expand_img', $te);
			tool.$pet = $('.Streams_participants_expand_text', $te);
			tool.$pc = $('.Streams_participants_container', $te);
			tool.$avatars = $('.Streams_participants_avatars', $te);
			tool.$blanks = $('.Streams_participants_blanks', $te);
			_continue();
			return false;
		}

		tool.$count = $("");
		tool.$max = $("");
		tool.$pet = $("See All");
		tool.$pei = $('').attr({
			src: Q.url('{{Q}}/img/expand.png'),
			alt: "expand"
		});
		tool.$controls = $("
")
			.append(
				$("
")
				.append(tool.$pei, tool.$pet)
			).appendTo($te);
		tool.$summary = $("
")
			.append($('').append(tool.$count, tool.$max))
			.appendTo($te);
		tool.$pc = $("
")
			.appendTo($te);
		tool.$avatars = $("")
			.appendTo(tool.$pc);
		tool.$blanks = $("")
			.appendTo(tool.$pc);

		// set expand icon click event
		tool.$pei.plugin('Q/clickable').on(Q.Pointer.fastclick, function () {
			if (state.expanded) {
				tool.$blanks.show();
				$te.animate({height: state.originalHeight}, function () {
					state.expanded = false;
				});
				tool.$pei.attr({
					src: Q.url('{{Q}}/img/expand.png'),
					alt: 'expand'
				});
				tool.$pet.html('See All');
			} else {
				state.originalHeight = $te.height();
				tool.$blanks.hide();
				$te.animate({height: tool.$pc.height()}, function () {
					state.expanded = true;
				});
				tool.$pei.attr({
					src: Q.url('{{Q}}/img/collapse.png'),
					alt: 'collapse'
				});
				tool.$pet.html('Fewer');
			}
		});

		$te.addClass('Streams_participants_loading');
		Q.Streams.get(state.publisherId, state.streamName, function (err, stream, extra) {
			var fem = Q.firstErrorMessage(err);
			if (fem) {
				return console.warn("Streams/participants: " + fem);
			}
			if (!$(tool.element).closest('body').length) {
				return;
			}
			tool.stream = this;
			var i = 0, c = 0;
			$te.removeClass('Streams_participants_loading');
			Q.Tool.clear(tool.$avatars[0]);
			Q.Tool.clear(tool.$blanks[0]);
			tool.$avatars.empty();
			tool.$blanks.empty();
			Q.each(extra && extra.participants, function (userId, participant) {
				if (participant.state !== 'participating') {
					return;
				}
				++c;
				if (!state.maxShow || ++i <= state.maxShow) {
					_addAvatar(userId);
				}
			}, { sort: 'insertedTime', ascending: false });
			state.count = c;
			if (state.showBlanks) {
				Q.each(c, state.maxShow-1, 1, function () {
					_addAvatar('');
				});
			}
			_continue();

		}, {participants: state.maxLoad});
		return true;

		function _continue() {
			tool.stateChanged('count');

			tool.adjustInterval = setInterval(_adjustInterval, 500);

			if (state.max) {
				tool.$max.text('/' + state.max);
			}

			Q.Streams.retainWith(tool).get(state.publisherId, state.streamName, function () {
				var stream = this;
				stream.onMessage("Streams/join")
				.set(function (stream, message, messages) {
					_addAvatar(message.byUserId, true);
					++tool.state.count;
					tool.stateChanged('count');
				}, tool);
				stream.onMessage("Streams/leave")
				.set(function (stream, message, messages) {
					_removeAvatar(message.byUserId);
					--tool.state.count;
					tool.stateChanged('count');
				}, tool);
				var si = state.invite;
				if (!si || !stream.testAdminLevel('invite')) {
					Q.handle(callback, tool, []);
					return Q.handle(state.onRefresh, tool, []);
				}
				if (tool.$('.Streams_inviteTrigger').length) {
					return; // the invite button already rendered
				}
				Q.Text.get("Streams/content", function (err, result) {
					var text = result && result.invite;
					if (text) {
						state.templates.invite.fields.alt = text.command;
						state.templates.invite.fields.title = text.command;
					}
					Q.Template.render(
						'Streams/participants/invite',
						state.templates.invite.fields,
						function (err, html) {
							if (err) return;
							var $element = tool.$invite = $(html).insertBefore(tool.$avatars);
							var filter = '.Streams_inviteTrigger';
							$(tool.element).on(Q.Pointer.fastclick, filter, function () {
								var options = Q.extend({
									identifier: si.identifier
								}, si);
								Q.Streams.invite(
									state.publisherId,
									state.streamName,
									options,
									function (err, data) {
										state.onInvited.handle.call(tool, err, data);
									}
								);
								return false;
							}).on(Q.Pointer.click, filter, function () {
								return false;
							}).on(Q.Pointer.start.eventName, filter, function () {
								$(tool.element).addClass('Q_discouragePointerEvents');
								function _pointerEndHandler() {
									$(tool.element).removeClass('Q_discouragePointerEvents');
									$(window).off(Q.Pointer.end, _pointerEndHandler);
								}
								$(window).on(Q.Pointer.end, _pointerEndHandler);
							});

							if (si.clickable) {
								$('img', $element).plugin(
									'Q/clickable', Q.extend({
										triggers: $element
									}, si.clickable)
								);
							}
							Q.handle(callback, tool, []);
							Q.handle(state.onRefresh, tool, []);
						},
						state.templates.invite
					);
				});
			});
		}

		function _addAvatar(userId, prepend) {
			var $e = userId ? tool.$avatars : tool.$blanks;
			if (userId && $(".Users_avatar_tool[id*=" + userId + "]", $e).length) {
				return;
			}

			var $element = $(Q.Tool.setUpElement(
				'div',
				'Users/avatar',
				Q.extend({}, state.avatar, {
					userId: userId,
				}),
				userId || null,
				tool.prefix)
			);
			if (false === Q.handle(state.filter, tool, [userId, $element[0]])) {
				return;
			}

			$element[prepend?'prependTo':'appendTo']($e).activate(function () {
				tool.orderAvatars();
			});
		}

		function _removeAvatar(userId) {
			var $element = tool.$elements[userId];
			if ($element) {
				Q.removeElement($element[0], true);
			}
		}

		function _adjustInterval() {
			var w = $te.width();
			var pm = tool.$pc.outerWidth(true) - tool.$pc.width();
			if (state.showSummary) {
				w = w - tool.$summary.outerWidth(true);
			}
			if (state.showControls) {
				w = w - tool.$controls.outerWidth(true);
				var $pcw = tool.$pc.innerWidth();
				var avatarsWidth = 0;
				$(".Streams_participants_invite", tool.$pc).add(".Users_avatar_tool", tool.$avatars).each(function () {
					avatarsWidth += $(this).outerWidth(true);
				});
				var overflowed = ($pcw > 0 && avatarsWidth > $pcw);
				if (overflowed) {
					if (!state.overflowed) {
						$te.addClass('Q_overflowed');
					}
				} else {
					$te.removeClass('Q_overflowed');
					tool.$blanks.show();
				}
				state.overflowed = overflowed;
			}

			tool.$pc.width(w - pm);
		}
	},
	/**
	 * Reorder avatars according to state.orders array
	 * @method orderAvatars
	 */
	orderAvatars: function () {
		var tool = this;
		var state = this.state;
		var $avatars = $(".Users_avatar_tool", tool.$avatars);

		// if state.ordering array empty, this method have no sense
		if (Q.isEmpty(state.ordering)) {
			return;
		}

		$avatars.each(function () {
			var avatarTool = Q.Tool.from(this, "Users/avatar");
			if (!avatarTool) {
				return;
			}

			var userId = avatarTool.state.userId;
			var $element = $(this);

			if (state.ordering.includes(userId)) {
				var elementInsertAfter = null;
				var $avatars = $(".Users_avatar_tool", tool.$avatars);

				for (var i = 0; i < state.ordering.length; i++) {
					if (state.ordering[i] === userId) {
						elementInsertAfter ? $element.insertAfter(elementInsertAfter) : $element.prependTo(tool.$avatars);
						break;
					}

					$avatars.each(function () {
						if (Q.getObject("state.userId", Q.Tool.from(this, "Users/avatar")) === state.ordering[i]) {
							elementInsertAfter = this;
						}
					});
				}

				$element.addClass("Streams_participants_ordered");
			} else {
				var $lastOrdered = $(".Streams_participants_ordered", tool.$avatars).last();
				if ($lastOrdered.length) {
					$element.insertAfter($lastOrdered);
				} else {
					$element.prependTo(tool.$avatars);
				}
			}
		});
	}
});

Q.Template.set('Streams/participants/invite',
	'
' +
	'	' +
	'
{{& title}}
' +
	'
'
);

})(Q, jQuery);<|endoftext|>


<commit_before>$( document ).ready(function() {

  $.ajax({
    url: "https://paulcaronnews.tumblr.com/api/read/xml?num=10&&type=text",
    dataType: 'jsonp'
  }).done(function(data) {
    let posts = data.posts,
    blogsHtml = `

                  RSS Feed

`;
    for(let x in posts){
      blogsHtml +=
        `

${posts[x]["regular-title"]} ${posts[x]["date"]}

            ${posts[x]["regular-body"]}

`;
    }
    blogsHtml += `

        For more see the archives ...

`
    $('#blogDiv').html(blogsHtml)
    // blogDiv
    console.log(data)
  });

});
<commit_msg>remove console<commit_after>$( document ).ready(function() {

  $.ajax({
    url: "https://paulcaronnews.tumblr.com/api/read/xml?num=10&&type=text",
    dataType: 'jsonp'
  }).done(function(data) {
    let posts = data.posts,
    blogsHtml = `

                  RSS Feed

`;
    for(let x in posts){
      blogsHtml +=
        `

${posts[x]["regular-title"]} ${posts[x]["date"]}

            ${posts[x]["regular-body"]}

`;
    }
    blogsHtml += `

        For more see the archives ...

`
    $('#blogDiv').html(blogsHtml)
  });

});
<|endoftext|>


<commit_before>var toArray = require('../lang/toArray')
var testval = require('../lang/testval')
var filter  = require('./filter')

function without (arr, removeItems) {
  removeItems = toArray(removeItems) // check this for a string of args

  return filter(arr, function (item) {
    return !testval(item, removeItems)
  })
}

module.exports = without
<commit_msg>update array/without -- use array/convert<commit_after>var convert = require('./convert')
var filter  = require('./filter')
var testval = require('../lang/testval')

function without (arr, removeItems) {
  removeItems = convert.toArray(removeItems)

  return filter(arr, function (item) {
    return !testval(item, removeItems)
  })
}

module.exports = without
<|endoftext|>


<commit_before>function goUrl(url, iframeId) {
	if(iframeId) {
		document.getElementById(iframeId).setAttribute('src', url);
		return true;
	}
	window.location.href = url;
}
function PrefixInteger(num, n) {
	/*
	 * num传入的数字，n需要的字符长度
	 * 例如：传入6，需要的字符长度为3，调用方法后字符串结果为：006
	 */
	return(Array(n).join(0) + num).slice(-n);
}
function ListingTag(id) {
	/*
	 * TAB页面切换
	 */
	var nav = document.getElementById(id);
	var li = nav.getElementsByTagName('li');
	for(i = 0; i < li.length; i++) {
		li[i].onclick = function() {
//			console.log(this.innerHTML);
			var li = this.parentElement.getElementsByTagName('li');
			for(j = 0; j < li.length; j++) {
				li[j].className = li[j].className.replace('active','');
				try {
					var elm = document.getElementById(li[j].getAttribute('data-go'));
					elm.style.display = 'none';
				}catch(e){

				}
			}
			this.className += ' active';
			var elm = document.getElementById(this.getAttribute('data-go'));
			if(elm){
				elm.style.display = 'block';
			}
			var fun = this.getAttribute('data-fun');
			if(fun){
				try{
					eval(''+fun+'');
				}catch(e){

				}
			}
			var loadFun = this.getAttribute('data-loadFun');
			if(loadFun){
				this.removeAttribute('data-loadFun');
				try{
					eval(''+loadFun+'');
				}catch(e){

				}
			}
		}
	}
	li[0].click();
}
function errorAction(codeData){
	switch(codeData.code*1){
		case 301:
			try{
				window.parent.location.href=codeData.url;
			}catch(e){
				try{
					window.location.href=codeData.url;
				}catch(e2){
					showMsg(codeData.Msg);
				}
			}
		break;
		case 201:
			showMsg(codeData.Msg);
		break;
		default:
		showMsg(codeData.Msg);
	}
}
function showMsg(text){
	try{
		window.parent.easyDialog.open({
			container : {
				header : '信息提示',
				content : text,
				yesFn : function(){
					return true;
				},
				noFn : true
			}
		});
	}catch(e){
		try{
			window.easyDialog.open({
				container : {
					header : '信息提示',
					content : text,
					yesFn : function(){
						return true;
					},
					noFn : true
				}
			});
		}catch(e2){
			alert(text);
		}
	}
}
<commit_msg>修复提示框按钮问题<commit_after>function goUrl(url, iframeId) {
	if(iframeId) {
		document.getElementById(iframeId).setAttribute('src', url);
		return true;
	}
	window.location.href = url;
}
function PrefixInteger(num, n) {
	/*
	 * num传入的数字，n需要的字符长度
	 * 例如：传入6，需要的字符长度为3，调用方法后字符串结果为：006
	 */
	return(Array(n).join(0) + num).slice(-n);
}
function ListingTag(id) {
	/*
	 * TAB页面切换
	 */
	var nav = document.getElementById(id);
	var li = nav.getElementsByTagName('li');
	for(i = 0; i < li.length; i++) {
		li[i].onclick = function() {
//			console.log(this.innerHTML);
			var li = this.parentElement.getElementsByTagName('li');
			for(j = 0; j < li.length; j++) {
				li[j].className = li[j].className.replace('active','');
				try {
					var elm = document.getElementById(li[j].getAttribute('data-go'));
					elm.style.display = 'none';
				}catch(e){

				}
			}
			this.className += ' active';
			var elm = document.getElementById(this.getAttribute('data-go'));
			if(elm){
				elm.style.display = 'block';
			}
			var fun = this.getAttribute('data-fun');
			if(fun){
				try{
					eval(''+fun+'');
				}catch(e){

				}
			}
			var loadFun = this.getAttribute('data-loadFun');
			if(loadFun){
				this.removeAttribute('data-loadFun');
				try{
					eval(''+loadFun+'');
				}catch(e){

				}
			}
		}
	}
	li[0].click();
}
function errorAction(codeData){
	switch(codeData.code*1){
		case 301:
			try{
				window.parent.location.href=codeData.url;
			}catch(e){
				try{
					window.location.href=codeData.url;
				}catch(e2){
					showMsg(codeData.Msg);
				}
			}
		break;
		case 201:
			showMsg(codeData.Msg);
		break;
		default:
		showMsg(codeData.Msg);
	}
}
function showMsg(text){
	try{
		window.parent.easyDialog.open({
			container : {
				header : '信息提示',
				content : text,
				yesFn : function(){
					return true;
				},
				noFn : false
			}
		});
	}catch(e){
		try{
			window.easyDialog.open({
				container : {
					header : '信息提示',
					content : text,
					yesFn : function(){
						return true;
					},
					noFn : false
				}
			});
		}catch(e2){
			alert(text);
		}
	}
}
<|endoftext|>


<commit_before>//increment
export function increment(index) {
  return {
    type: "INCREMENT_LIKES",
    index
  }
}

//add comment
export function addComment(postId, author, comment) {
  return{
    type: "ADD_COMMENT",
    postId,
    author,
    comment

  }
}

//remove comment

export function removeComment(postId, i) {
  type: "REMOVE_COMMENT",
  i,
  postId
}
<commit_msg>Modified Remove comment action<commit_after>//increment
export function increment(index) {
  return {
    type: "INCREMENT_LIKES",
    index
  }
}

//add comment
export function addComment(postId, author, comment) {
  return{
    type: "ADD_COMMENT",
    postId,
    author,
    comment

  }
}

//remove comment

export function removeComment(postId, i) {
  return{
    type: "REMOVE_COMMENT",
    i,
    postId
  }
}
<|endoftext|>


<commit_before>import React from 'react';
import { Button } from 'react-bootstrap';

class FriendsPref extends React.Component {
  constructor(){
    super();
    this.displayRestaurantResults = this.displayRestaurantResults.bind(this);
  }

  displayRestaurantResults(e){
    e.preventDefault();
    const { displayResults } = this.props.viewActions;

    displayResults();
  }

  render(){
    return (

Select who youd like to eat with

        <Button onClick={this.displayRestaurantResults}>Eat with Hoon</Button>

    )
  }
}

export default FriendsPref;
<commit_msg>[pull] fixes merge conflict<commit_after>import React from 'react';
import { Button } from 'react-bootstrap';

class FriendsPref extends React.Component {
  constructor(){
    super();
    this.displayRestaurantResults = this.displayRestaurantResults.bind(this);
  }

  displayRestaurantResults(e){
    e.preventDefault();
    const { displayResults } = this.props.viewActions;

    displayResults();
  }

  render(){
    console.log('props in friends pref', this.props);
    return (

Select who youd like to eat with

        <Button onClick={this.displayRestaurantResults}>Eat with Hoon</Button>

    )
  }
}

export default FriendsPref;
<|endoftext|>


<commit_before>const bMin = document.querySelector('.b--min');
const sMin = document.querySelector('.s--min');
const bPlus = document.querySelector('.b--plus');
const sPlus = document.querySelector('.s--plus');
const sSes = document.querySelector('.span--session');
const sBreak = document.querySelector('.span--break');
const pTimer = document.querySelector('.p--timer');
const bTimer = document.querySelector('.b--timer');
const h2Type = document.querySelector('.h2--type');

const minOrPlus = (op, n)=>{
  if(op === 'min' && n.innerHTML > 1)
    n.innerHTML -= 1;
  else if(op == 'plus' && n.innerHTML < 60)
    n.innerHTML = +n.innerHTML + 1;

  pTimer.innerHTML = sSes.innerHTML + ':00';
};

//FUNCTIONS FOR TIMER
//
const setSecs = (t, mins, secs)=>{
  if(secs > 10)
    return mins + ':' + (secs-1).toString();

  else if(secs > 0)
    return mins + ':0' + (secs-1);

  else
    return setMins(t, mins);
};

const setMins = (t, mins)=>{
  if(mins > 10)
    return (mins-1).toString() + ':59';

  else if(mins > 0)
    return (mins-1).toString() + ':59';

  else
    return end(t);
};

const end = (t)=>{
  new Audio('assets/beep.mp3').play();

  if(t === 'break'){
    h2Type.innerHTML = 'In Break';
    return (sBreak.innerHTML-1).toString() + ':59';
  }

  else{
    h2Type.innerHTML = 'In Session';
    return (sSes.innerHTML-1) + ':59';
  }

};

const run = ()=>{
  bMin.disabled = sMin.disabled = bPlus.disabled = sPlus.disabled = true;

  const intervalID = setInterval(()=>{
    if(h2Type.innerHTML === 'In Session'){
      pTimer.innerHTML =
        setSecs('break',
          pTimer.innerHTML.split(':')[0],
          pTimer.innerHTML.split(':')[1]);
    }
    else{
      pTimer.innerHTML =
        setSecs('session',
          pTimer.innerHTML.split(':')[0],
          pTimer.innerHTML.split(':')[1]);
    }

    bTimer.onclick = ()=>{
      clearInterval(intervalID);
      reset();
      h2Type.innerHTML = 'In Session';

//have to set it again cuz it won't go to the bottom bTimer.onclick function :D
      bTimer.onclick = ()=>{run();};
    };
  }, 1000);
};

const reset = ()=>{
  bMin.disabled = sMin.disabled = bPlus.disabled = sPlus.disabled = false;
  pTimer.innerHTML = sSes.innerHTML + ':00';
};

//
//--end of functions for timer

//Settings
bMin.onclick = ()=>{minOrPlus('min', sBreak);};
sMin.onclick = ()=>{minOrPlus('min', sSes);};
bPlus.onclick = ()=>{minOrPlus('plus', sBreak);};
sPlus.onclick = ()=>{minOrPlus('plus', sSes);};

//Timer
bTimer.onclick = ()=>{run();};
<commit_msg>fixed inactive tab beep<commit_after>const bMin = document.querySelector('.b--min');
const sMin = document.querySelector('.s--min');
const bPlus = document.querySelector('.b--plus');
const sPlus = document.querySelector('.s--plus');
const sSes = document.querySelector('.span--session');
const sBreak = document.querySelector('.span--break');
const pTimer = document.querySelector('.p--timer');
const bTimer = document.querySelector('.b--timer');
const h2Type = document.querySelector('.h2--type');
const beep = new Audio('assets/beep.mp3');
beep.volume = 1;

const minOrPlus = (op, n)=>{
  if(op === 'min' && n.innerHTML > 1)
    n.innerHTML -= 1;
  else if(op == 'plus' && n.innerHTML < 60)
    n.innerHTML = +n.innerHTML + 1;

  pTimer.innerHTML = sSes.innerHTML + ':00';
};

//FUNCTIONS FOR TIMER
//
const setSecs = (t, mins, secs)=>{
  if(secs > 10)
    return mins + ':' + (secs-1).toString();

  else if(secs > 0)
    return mins + ':0' + (secs-1);

  else
    return setMins(t, mins);
};

const setMins = (t, mins)=>{
  if(mins > 10)
    return (mins-1).toString() + ':59';

  else if(mins > 0)
    return (mins-1).toString() + ':59';

  else
    return end(t);
};

const end = (t)=>{
  beep.play();

  if(t === 'break'){
    h2Type.innerHTML = 'In Break';
    return (sBreak.innerHTML-1).toString() + ':59';
  }

  else{
    h2Type.innerHTML = 'In Session';
    return (sSes.innerHTML-1) + ':59';
  }

};

const run = ()=>{
  bMin.disabled = sMin.disabled = bPlus.disabled = sPlus.disabled = true;

  const intervalID = setInterval(()=>{
    if(h2Type.innerHTML === 'In Session'){
      pTimer.innerHTML =
        setSecs('break',
          pTimer.innerHTML.split(':')[0],
          pTimer.innerHTML.split(':')[1]);
    }
    else{
      pTimer.innerHTML =
        setSecs('session',
          pTimer.innerHTML.split(':')[0],
          pTimer.innerHTML.split(':')[1]);
    }

    bTimer.onclick = ()=>{
      clearInterval(intervalID);
      reset();
      h2Type.innerHTML = 'In Session';

//have to set it again cuz it won't go to the bottom bTimer.onclick function :D
      bTimer.onclick = ()=>{run();};
    };
  }, 1000);
};

const reset = ()=>{
  bMin.disabled = sMin.disabled = bPlus.disabled = sPlus.disabled = false;
  pTimer.innerHTML = sSes.innerHTML + ':00';
};

//
//--end of functions for timer

//Settings
bMin.onclick = ()=>{minOrPlus('min', sBreak);};
sMin.onclick = ()=>{minOrPlus('min', sSes);};
bPlus.onclick = ()=>{minOrPlus('plus', sBreak);};
sPlus.onclick = ()=>{minOrPlus('plus', sSes);};

//Timer
bTimer.onclick = ()=>{run();};
<|endoftext|>


<commit_before>// Run all of this when the document's ready
$(function(){

  // Load all of our UI elements here so that they're cached and super fast
  var metaform = $('#metaform'),
      wordEntry = $('#word-entry'),
      btnEntry = $('.btn-entry'),
      question = $('#question'),
      answer = $('#answer'),
      answerWord = $('#word'),
      answerMetaphor = $('#metaphor'),
      answerArticle = $('#article'),
      twitterShare = $('#twitter-share'),
      title = $('title');

  // Used later for history state changes
  var metaphorUrlRegex = /metaphor\/(.+)\/(.+)/;

  var showAnswer = function(word, metaphor){
    var metaphorSlug = metaphor.replace(/\s/g, '-'),
        wordSlug = word.replace(/\s/g, '-'),
        article = 'a';

    if(metaphor[0].match(/[aeiou]/)){
      article = 'an';
    }

    // Set the text
    answerWord.text(word);
    answerMetaphor.text(metaphor);
    answerArticle.text(article);

    title.text('Metaphorgy: My ' + word + ' is ' + article + ' ' + metaphor);

    twitterShare.attr('href',
      'http://twitter.com/intent/tweet?url=http://www.metaphor.gy/metaphor/' + wordSlug + '/' + metaphorSlug +
        '&text=My ' + word + ' is ' + metaphor + '.&hashtags=metaphorgy');

    // Toggle all the classes, and undisable the button
    btnEntry.addClass('btn-success3d').removeAttr('disabled');
    question.addClass('hidden');
    answer.removeClass('hidden');
  }

  // Reset the hidden classes and whatnot
  var resetForm = function(){
    wordEntry.val('');
    btnEntry.removeClass('btn-success3d');
    question.removeClass('hidden');
    answer.addClass('hidden');

    title.text('Metaphorgy: Metaphor Generator');
  }

  // When the button is clicked, either submit the form or reset the state
  btnEntry.on('click', function(e){
    e.preventDefault();

    if(!btnEntry.attr('disabled')){
      if(btnEntry.hasClass('btn-success3d')){
        // Save it in the history so it looks like  a legit url
        if(history && history.pushState){
          history.pushState(null, null, '/');
        }

        // Reset the form, remove classes and whatnot
        resetForm();
      }else{
        // Otherwise, go ahead and submit the form
        metaform.submit();
      }
    }
  });

  // When the form is submitted, get the data
  metaform.on('submit', function(e){
    // Prevent the form from actually submitting; ajax instead
    e.preventDefault();

    var word = wordEntry.val();

    // Only continue if word isn't empty
    if(word != ""){
      btnEntry.removeClass('btn-error3d');

      // Only do a thing if we're not waiting on a response
      if(!btnEntry.attr('disabled')){

        // Set the button to disabled while it loads
        btnEntry.attr('disabled', 'disabled');

        // Load the metaphor from the server
        $.getJSON('/metaphor/' + encodeURIComponent(word.replace(/\s/g, '-')), function(data){
          if(data.metaphor){
            var metaphorSlug = encodeURIComponent(data.metaphor.replace(/\s/g, '-')),
                wordSlug = encodeURIComponent(word.replace(/\s/g, '-'));

            // Save it in the history so it looks like  a legit url
            if(history && history.pushState){
              history.pushState(null, null, '/metaphor/' + wordSlug + '/' + metaphorSlug);
            }

            // Track the metaphor in google analytics, if available
            if(typeof ga != 'undefined' && ga){
              ga('send', 'event', 'metaphor', 'create', word);
            }

            // Show the answer in the fields
            showAnswer(data.word, data.metaphor);
          }else{
            btnEntry.addClass('btn-error3d').removeAttr('disabled');
          }
        });
      }
    }
  });

  var stateChange = function(path, fn){

  }

  var handleHistoryChange = function(e) {
    var regexResult = metaphorUrlRegex.exec(location.pathname);

    if(regexResult && regexResult.length == 3){
      var word = decodeURIComponent(regexResult[1]).replace(/-/g, ' '),
          metaphor = decodeURIComponent(regexResult[2]).replace(/-/g, ' ');

      showAnswer(word, metaphor);
    }else{
      resetForm();
    };
  }

  // When the history changes, load the metaphor or reset the form. Watch for
  // history changes.
  window.addEventListener("popstate", handleHistoryChange);
});

<commit_msg>fix missing article in tweets<commit_after>// Run all of this when the document's ready
$(function(){

  // Load all of our UI elements here so that they're cached and super fast
  var metaform = $('#metaform'),
      wordEntry = $('#word-entry'),
      btnEntry = $('.btn-entry'),
      question = $('#question'),
      answer = $('#answer'),
      answerWord = $('#word'),
      answerMetaphor = $('#metaphor'),
      answerArticle = $('#article'),
      twitterShare = $('#twitter-share'),
      title = $('title');

  // Used later for history state changes
  var metaphorUrlRegex = /metaphor\/(.+)\/(.+)/;

  var showAnswer = function(word, metaphor){
    var metaphorSlug = metaphor.replace(/\s/g, '-'),
        wordSlug = word.replace(/\s/g, '-'),
        article = 'a';

    if(metaphor[0].match(/[aeiou]/)){
      article = 'an';
    }

    // Set the text
    answerWord.text(word);
    answerMetaphor.text(metaphor);
    answerArticle.text(article);

    title.text('Metaphorgy: My ' + word + ' is ' + article + ' ' + metaphor);

    twitterShare.attr('href',
      'http://twitter.com/intent/tweet?url=http://www.metaphor.gy/metaphor/' + wordSlug + '/' + metaphorSlug +
        '&text=My ' + word + ' is ' + article + ' ' + metaphor + '.&hashtags=metaphorgy');

    // Toggle all the classes, and undisable the button
    btnEntry.addClass('btn-success3d').removeAttr('disabled');
    question.addClass('hidden');
    answer.removeClass('hidden');
  }

  // Reset the hidden classes and whatnot
  var resetForm = function(){
    wordEntry.val('');
    btnEntry.removeClass('btn-success3d');
    question.removeClass('hidden');
    answer.addClass('hidden');

    title.text('Metaphorgy: Metaphor Generator');
  }

  // When the button is clicked, either submit the form or reset the state
  btnEntry.on('click', function(e){
    e.preventDefault();

    if(!btnEntry.attr('disabled')){
      if(btnEntry.hasClass('btn-success3d')){
        // Save it in the history so it looks like  a legit url
        if(history && history.pushState){
          history.pushState(null, null, '/');
        }

        // Reset the form, remove classes and whatnot
        resetForm();
      }else{
        // Otherwise, go ahead and submit the form
        metaform.submit();
      }
    }
  });

  // When the form is submitted, get the data
  metaform.on('submit', function(e){
    // Prevent the form from actually submitting; ajax instead
    e.preventDefault();

    var word = wordEntry.val();

    // Only continue if word isn't empty
    if(word != ""){
      btnEntry.removeClass('btn-error3d');

      // Only do a thing if we're not waiting on a response
      if(!btnEntry.attr('disabled')){

        // Set the button to disabled while it loads
        btnEntry.attr('disabled', 'disabled');

        // Load the metaphor from the server
        $.getJSON('/metaphor/' + encodeURIComponent(word.replace(/\s/g, '-')), function(data){
          if(data.metaphor){
            var metaphorSlug = encodeURIComponent(data.metaphor.replace(/\s/g, '-')),
                wordSlug = encodeURIComponent(word.replace(/\s/g, '-'));

            // Save it in the history so it looks like  a legit url
            if(history && history.pushState){
              history.pushState(null, null, '/metaphor/' + wordSlug + '/' + metaphorSlug);
            }

            // Track the metaphor in google analytics, if available
            if(typeof ga != 'undefined' && ga){
              ga('send', 'event', 'metaphor', 'create', word);
            }

            // Show the answer in the fields
            showAnswer(data.word, data.metaphor);
          }else{
            btnEntry.addClass('btn-error3d').removeAttr('disabled');
          }
        });
      }
    }
  });

  var stateChange = function(path, fn){

  }

  var handleHistoryChange = function(e) {
    var regexResult = metaphorUrlRegex.exec(location.pathname);

    if(regexResult && regexResult.length == 3){
      var word = decodeURIComponent(regexResult[1]).replace(/-/g, ' '),
          metaphor = decodeURIComponent(regexResult[2]).replace(/-/g, ' ');

      showAnswer(word, metaphor);
    }else{
      resetForm();
    };
  }

  // When the history changes, load the metaphor or reset the form. Watch for
  // history changes.
  window.addEventListener("popstate", handleHistoryChange);
});

<|endoftext|>


<commit_before>Template.postSubmit.events({
  'submit form': function(e) {
    e.preventDefault();

    var post = {
      url: $(e.target).find('[name=url]').val(),
      title: $(e.target).find('[name=title]').val(),
      message: $(e.target).find('[name=message]').val()
    }

    Meteor.call('post', post, function(error, id) {
      if (error)
        return alert(error.reason);
    });
      Router.go('postsList');
  }
});<commit_msg>Actually use the error reporting<commit_after>Template.postSubmit.events({
  'submit form': function(e) {
    e.preventDefault();

    var post = {
      url: $(e.target).find('[name=url]').val(),
      title: $(e.target).find('[name=title]').val(),
      message: $(e.target).find('[name=message]').val()
    }

    Meteor.call('post', post, function(error, id) {
      if (error) {
        throwError(error.reason);

        if (error.error === 302)
          Router.go('postPage', {_id: error.details})

      } else {
        Router.go('postPage', {_id: id});
      }
    });
  }
});<|endoftext|>


<commit_before>/**
* @namespace basis.tracker
*/

var hasOwnProperty = Object.prototype.hasOwnProperty;
var eventUtils = require('basis.dom.event');
var getComputedStyle = require('basis.dom.computedStyle').get;
var getBoundingRect = require('basis.layout').getBoundingRect;
var tracker = new basis.Token();
var selectorMap = {};
var eventMap = {};

var VISIBLE_CHECK_INTERVAL = 250;

function track(event){
  try {
    tracker.set(event);
  } catch(e) {
    /** @cut */ basis.dev.error('Error during tracking event processing', event, e);
  }
}

// ui activity
// resource('./ui.js').ready(function(exports){
//   [exports.Node, exports.PartitionNode].forEach(function(Class){
//     Class.extend(function(super_, current_){
//       return {
//         templateAction: function(actionName, event){
//           if (tracker.handler)
//           {
//             var cursor = event.actionTarget;
//             var path = [];
//             var role;

//             if (cursor.hasAttribute('role-marker'))
//             {
//               while (cursor && cursor !== document)
//               {
//                 var role = cursor.getAttribute('role-marker');
//                 if (role)
//                   path.unshift(role);
//                 cursor = cursor.parentNode;
//               }

//               track({
//                 type: 'ui',
//                 path: path,
//                 event: event.type,
//                 action: actionName
//               });
//             }
//           }

//           current_.templateAction.call(this, actionName, event);
//         }
//       };
//     });
//   });
// });

// net activity
// resource('./net.js').ready(function(exports){
//   var trackEvents = ['start', 'success', 'failure', 'abort'];

//   exports.transportDispatcher.addHandler({
//     '*': function(event){
//       var request = event.args[1];
//       if (request instanceof exports.AbstractRequest &&
//           trackEvents.indexOf(event.type) != -1)
//         track({
//           type: 'net',
//           path: [request.requestData.url],
//           event: event.type
//         });
//     }
//   });
// });

//
// show
//

var checkTimer;

function checkShow(){
  function isVisible(element){
    if (getComputedStyle(element, 'visibility') != 'visible')
      return false;

    var box = getBoundingRect(element);

    if (!box.width || !box.height)
      return false;

    return true;
  }

  var list = getSelectorList('show');

  for (var i = 0; i < list.length; i++)
  {
    var selector = list[i];
    var elements = document.querySelectorAll(getCssSelectorFromPath(selector.selector, true));
    var visibleElement = basis.array.search(basis.array(elements), true, isVisible);
    var visible = Boolean(visibleElement);
    var state;

    if (!hasOwnProperty.call(list.visible, selector.selectorStr))
      state = list.visible[selector.selectorStr] = false;
    else
      state = list.visible[selector.selectorStr];

    list.visible[selector.selectorStr] = visible;

    if (state == false && visible)
      track({
        type: 'ui',
        path: stringifyPath(getPathByNode(visibleElement)),
        selector: selector.selectorStr,
        event: 'show',
        data: selector.data
      });
  }
}

//
// main API
//

var roleRegExp = /^(.+?)(?:\((.+)\))?$/;
var subroleRegExp = /\/([^\/\(\)]+)$/;
function parseRole(str){
  var role = '';
  var roleId = '';
  var subrole = '';
  var m;

  if (m = str.match(subroleRegExp))
  {
    subrole = m[1];
    str = str.substr(0, str.length - m[0].length);
  }

  if (m = str.match(roleRegExp))
  {
    role = m[1] || '';
    roleId = m[2] || '';
  }

  return {
    role: role,
    roleId: roleId,
    subrole: subrole
  };
}

function stringifyRole(role){
  if (typeof role == 'string')
    return role;

  if (!role)
    return '';

  return [
    role.role || '',
    role.roleId ? '(' + role.roleId + ')' : '',
    role.subrole ? '/' + role.subrole : ''
  ].join('');
}

function parsePath(value){
  if (!Array.isArray(value))
    value = String(value || '').trim().split(/\s+/);

  return value.map(function(part){
    if (typeof part == 'string')
      return parseRole(part);

    return part || {
      role: '',
      roleId: '',
      subrole: ''
    };
  });
}

function stringifyPath(path){
  if (typeof path == 'string')
    return path;

  return path.map(stringifyRole).join(' ');
}

function isPathMatchSelector(path, selector){
  function isMatch(path, selector){
    path = typeof path == 'string' ? parseRole(path) : path || '';
    selector = typeof selector == 'string' ? parseRole(selector) : selector || '';

    return selector.role == path.role &&
           (selector.roleId == '*' || selector.roleId == path.roleId) &&
           selector.subrole == path.subrole;
  }

  var pathIndex = path.length;
  var selectorIndex = selector.length;

  if (!selectorIndex)
    return true;

  if (!isMatch(path[--pathIndex], selector[--selectorIndex]))
    return false;

  while (pathIndex > 0 && selectorIndex > 0)
    if (!isMatch(path[--pathIndex], selector[--selectorIndex]))
      selectorIndex++;

  return selectorIndex === 0;
}

function getPathByNode(node){
  var cursor = node;
  var path = [];
  var role;

  while (cursor && cursor !== document)
  {
    if (role = cursor.getAttribute('role-marker'))
      path.unshift(parseRole(role));
    cursor = cursor.parentNode;
  }

  return path;
}

function escapeQuotes(value){
  return String(value).replace(/\"/g, '\\"');
}

function getCssSelectorFromPath(path, selector){
  return parsePath(path).map(function(role){
    if (!role.role)
      return '';

    var start = escapeQuotes(role.role);
    var end = (role.subrole ? '/' + escapeQuotes(role.subrole) : '') + '"]';

    if (role.roleId)
    {
      if (selector && role.roleId == '*')
        start = '[role-marker^="' + start + '("][role-marker$=")';
      else
        start = '[role-marker="' + start + '(' + escapeQuotes(role.roleId) + ')';
    }
    else
    {
      start = '[role-marker="' + start;
    }

    return start + end;
  }).join(' ');
}

function getSelectorList(eventName){
  if (hasOwnProperty.call(eventMap, eventName))
    return eventMap[eventName];

  var selectorList = eventMap[eventName] = [];

  switch (eventName) {
    case 'show':
      selectorList.visible = {};
      checkTimer = setInterval(checkShow, VISIBLE_CHECK_INTERVAL);
      break;

    default:
      eventUtils.addGlobalHandler(eventName, function(event){
        var path = event.path.reduce(function(res, node){
          var role = node.getAttribute ? node.getAttribute('role-marker') : null;
          if (role)
            res.unshift(parseRole(role));
          return res;
        }, []);

        if (path.length)
          selectorList.forEach(function(item){
            if (isPathMatchSelector(path, item.selector))
              track({
                type: 'ui',
                path: stringifyPath(path),
                selector: stringifyPath(item.selector),
                event: event.type,
                data: item.data
              });
          });
      });
  }

  return selectorList;
}

function getEventList(selector){
  var selectorStr = stringifyPath(selector);

  if (hasOwnProperty.call(selectorMap, selectorStr))
    return selectorMap[selectorStr];

  var eventList = selectorMap[selectorStr] = [];

  eventList.selectorStr = selectorStr;
  eventList.selector = selector;

  return eventList;
}

function registrateSelector(selector, eventName, data){
  var selectorList = getSelectorList(eventName);
  var eventList = getEventList(selector);
  var selectorStr = eventList.selectorStr;

  if (basis.array.search(selectorList, selectorStr, 'selectorStr'))
  {
    /** @cut */ basis.dev.warn('Duplicate selector for event `' + eventName + '`:' + selector);
    return;
  }

  selectorList.push({
    selector: selector,
    selectorStr: selectorStr,
    data: data
  });

  eventList.push({
    event: eventName,
    data: data
  });
}

function loadMap(map){
  if (!map)
  {
    /** @cut */ basis.dev.warn('Wrong value for map');
    return;
  }

  for (var key in map)
  {
    var eventsMap = map[key];

    if (!eventsMap)
    {
      /** @cut */ basis.dev.warn('Value of map should be an object for path: ' + key);
      continue;
    }

    var selector = parsePath(key);
    for (var events in eventsMap)
    {
      var data = eventsMap[events];

      events.trim().split(/\s+/).forEach(function(eventName){
        registrateSelector(selector, eventName, data);
      });
    }
  }
};

function getInfo(path){
  var result = [];

  if (typeof path == 'string')
    path = parsePath(path);

  for (var key in selectorMap)
    if (isPathMatchSelector(path, selectorMap[key].selector))
      result.push.apply(result, selectorMap[key].map(function(item){
        return basis.object.extend({
          selector: selectorMap[key].selector,
          selectorStr: selectorMap[key].selectorStr
        }, item);
      }));

  return result.length ? result : false;
}

module.exports = {
  parsePath: parsePath,
  parseRole: parseRole,
  stringifyPath: stringifyPath,
  stringifyRole: stringifyRole,

  getInfo: getInfo,
  getPathByNode: getPathByNode,
  isPathMatchSelector: isPathMatchSelector,

  loadMap: loadMap,
  attach: function(fn, context){
    tracker.attach(fn, context);
  },
  detach: function(fn, context){
    tracker.detach(fn, context);
  }
};
<commit_msg>basis.tracker: export getCssSelectorFromPath<commit_after>/**
* @namespace basis.tracker
*/

var hasOwnProperty = Object.prototype.hasOwnProperty;
var eventUtils = require('basis.dom.event');
var getComputedStyle = require('basis.dom.computedStyle').get;
var getBoundingRect = require('basis.layout').getBoundingRect;
var tracker = new basis.Token();
var selectorMap = {};
var eventMap = {};

var VISIBLE_CHECK_INTERVAL = 250;

function track(event){
  try {
    tracker.set(event);
  } catch(e) {
    /** @cut */ basis.dev.error('Error during tracking event processing', event, e);
  }
}

// ui activity
// resource('./ui.js').ready(function(exports){
//   [exports.Node, exports.PartitionNode].forEach(function(Class){
//     Class.extend(function(super_, current_){
//       return {
//         templateAction: function(actionName, event){
//           if (tracker.handler)
//           {
//             var cursor = event.actionTarget;
//             var path = [];
//             var role;

//             if (cursor.hasAttribute('role-marker'))
//             {
//               while (cursor && cursor !== document)
//               {
//                 var role = cursor.getAttribute('role-marker');
//                 if (role)
//                   path.unshift(role);
//                 cursor = cursor.parentNode;
//               }

//               track({
//                 type: 'ui',
//                 path: path,
//                 event: event.type,
//                 action: actionName
//               });
//             }
//           }

//           current_.templateAction.call(this, actionName, event);
//         }
//       };
//     });
//   });
// });

// net activity
// resource('./net.js').ready(function(exports){
//   var trackEvents = ['start', 'success', 'failure', 'abort'];

//   exports.transportDispatcher.addHandler({
//     '*': function(event){
//       var request = event.args[1];
//       if (request instanceof exports.AbstractRequest &&
//           trackEvents.indexOf(event.type) != -1)
//         track({
//           type: 'net',
//           path: [request.requestData.url],
//           event: event.type
//         });
//     }
//   });
// });

//
// show
//

var checkTimer;

function checkShow(){
  function isVisible(element){
    if (getComputedStyle(element, 'visibility') != 'visible')
      return false;

    var box = getBoundingRect(element);

    if (!box.width || !box.height)
      return false;

    return true;
  }

  var list = getSelectorList('show');

  for (var i = 0; i < list.length; i++)
  {
    var selector = list[i];
    var elements = document.querySelectorAll(getCssSelectorFromPath(selector.selector, true));
    var visibleElement = basis.array.search(basis.array(elements), true, isVisible);
    var visible = Boolean(visibleElement);
    var state;

    if (!hasOwnProperty.call(list.visible, selector.selectorStr))
      state = list.visible[selector.selectorStr] = false;
    else
      state = list.visible[selector.selectorStr];

    list.visible[selector.selectorStr] = visible;

    if (state == false && visible)
      track({
        type: 'ui',
        path: stringifyPath(getPathByNode(visibleElement)),
        selector: selector.selectorStr,
        event: 'show',
        data: selector.data
      });
  }
}

//
// main API
//

var roleRegExp = /^(.+?)(?:\((.+)\))?$/;
var subroleRegExp = /\/([^\/\(\)]+)$/;
function parseRole(str){
  var role = '';
  var roleId = '';
  var subrole = '';
  var m;

  if (m = str.match(subroleRegExp))
  {
    subrole = m[1];
    str = str.substr(0, str.length - m[0].length);
  }

  if (m = str.match(roleRegExp))
  {
    role = m[1] || '';
    roleId = m[2] || '';
  }

  return {
    role: role,
    roleId: roleId,
    subrole: subrole
  };
}

function stringifyRole(role){
  if (typeof role == 'string')
    return role;

  if (!role)
    return '';

  return [
    role.role || '',
    role.roleId ? '(' + role.roleId + ')' : '',
    role.subrole ? '/' + role.subrole : ''
  ].join('');
}

function parsePath(value){
  if (!Array.isArray(value))
    value = String(value || '').trim().split(/\s+/);

  return value.map(function(part){
    if (typeof part == 'string')
      return parseRole(part);

    return part || {
      role: '',
      roleId: '',
      subrole: ''
    };
  });
}

function stringifyPath(path){
  if (typeof path == 'string')
    return path;

  return path.map(stringifyRole).join(' ');
}

function isPathMatchSelector(path, selector){
  function isMatch(path, selector){
    path = typeof path == 'string' ? parseRole(path) : path || '';
    selector = typeof selector == 'string' ? parseRole(selector) : selector || '';

    return selector.role == path.role &&
           (selector.roleId == '*' || selector.roleId == path.roleId) &&
           selector.subrole == path.subrole;
  }

  var pathIndex = path.length;
  var selectorIndex = selector.length;

  if (!selectorIndex)
    return true;

  if (!isMatch(path[--pathIndex], selector[--selectorIndex]))
    return false;

  while (pathIndex > 0 && selectorIndex > 0)
    if (!isMatch(path[--pathIndex], selector[--selectorIndex]))
      selectorIndex++;

  return selectorIndex === 0;
}

function getPathByNode(node){
  var cursor = node;
  var path = [];
  var role;

  while (cursor && cursor !== document)
  {
    if (role = cursor.getAttribute('role-marker'))
      path.unshift(parseRole(role));
    cursor = cursor.parentNode;
  }

  return path;
}

function escapeQuotes(value){
  return String(value).replace(/\"/g, '\\"');
}

function getCssSelectorFromPath(path, selector){
  return parsePath(path).map(function(role){
    if (!role.role)
      return '';

    var start = escapeQuotes(role.role);
    var end = (role.subrole ? '/' + escapeQuotes(role.subrole) : '') + '"]';

    if (role.roleId)
    {
      if (selector && role.roleId == '*')
        start = '[role-marker^="' + start + '("][role-marker$=")';
      else
        start = '[role-marker="' + start + '(' + escapeQuotes(role.roleId) + ')';
    }
    else
    {
      start = '[role-marker="' + start;
    }

    return start + end;
  }).join(' ');
}

function getSelectorList(eventName){
  if (hasOwnProperty.call(eventMap, eventName))
    return eventMap[eventName];

  var selectorList = eventMap[eventName] = [];

  switch (eventName) {
    case 'show':
      selectorList.visible = {};
      checkTimer = setInterval(checkShow, VISIBLE_CHECK_INTERVAL);
      break;

    default:
      eventUtils.addGlobalHandler(eventName, function(event){
        var path = event.path.reduce(function(res, node){
          var role = node.getAttribute ? node.getAttribute('role-marker') : null;
          if (role)
            res.unshift(parseRole(role));
          return res;
        }, []);

        if (path.length)
          selectorList.forEach(function(item){
            if (isPathMatchSelector(path, item.selector))
              track({
                type: 'ui',
                path: stringifyPath(path),
                selector: stringifyPath(item.selector),
                event: event.type,
                data: item.data
              });
          });
      });
  }

  return selectorList;
}

function getEventList(selector){
  var selectorStr = stringifyPath(selector);

  if (hasOwnProperty.call(selectorMap, selectorStr))
    return selectorMap[selectorStr];

  var eventList = selectorMap[selectorStr] = [];

  eventList.selectorStr = selectorStr;
  eventList.selector = selector;

  return eventList;
}

function registrateSelector(selector, eventName, data){
  var selectorList = getSelectorList(eventName);
  var eventList = getEventList(selector);
  var selectorStr = eventList.selectorStr;

  if (basis.array.search(selectorList, selectorStr, 'selectorStr'))
  {
    /** @cut */ basis.dev.warn('Duplicate selector for event `' + eventName + '`:' + selector);
    return;
  }

  selectorList.push({
    selector: selector,
    selectorStr: selectorStr,
    data: data
  });

  eventList.push({
    event: eventName,
    data: data
  });
}

function loadMap(map){
  if (!map)
  {
    /** @cut */ basis.dev.warn('Wrong value for map');
    return;
  }

  for (var key in map)
  {
    var eventsMap = map[key];

    if (!eventsMap)
    {
      /** @cut */ basis.dev.warn('Value of map should be an object for path: ' + key);
      continue;
    }

    var selector = parsePath(key);
    for (var events in eventsMap)
    {
      var data = eventsMap[events];

      events.trim().split(/\s+/).forEach(function(eventName){
        registrateSelector(selector, eventName, data);
      });
    }
  }
};

function getInfo(path){
  var result = [];

  if (typeof path == 'string')
    path = parsePath(path);

  for (var key in selectorMap)
    if (isPathMatchSelector(path, selectorMap[key].selector))
      result.push.apply(result, selectorMap[key].map(function(item){
        return basis.object.extend({
          selector: selectorMap[key].selector,
          selectorStr: selectorMap[key].selectorStr
        }, item);
      }));

  return result.length ? result : false;
}

module.exports = {
  parsePath: parsePath,
  parseRole: parseRole,
  stringifyPath: stringifyPath,
  stringifyRole: stringifyRole,

  getInfo: getInfo,
  getPathByNode: getPathByNode,
  isPathMatchSelector: isPathMatchSelector,
  getCssSelectorFromPath: getCssSelectorFromPath,

  loadMap: loadMap,
  attach: function(fn, context){
    tracker.attach(fn, context);
  },
  detach: function(fn, context){
    tracker.detach(fn, context);
  }
};
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012-2015 S-Core Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
* This file is supported of locale API
*
* @see support locale-sensitive languages.
* @since: 2015.10.27
* @author: minsung.jin
*/

define([
], function (
) {
    'use strict';

    function convertMessage(message, attr) {
        $('[' + attr + ']').each(function () {
            var key = $(this).attr(attr);
            $(this).text.call($(this), message[key]);
        });
    }

    return {
        convertMessage : convertMessage
    };
});
<commit_msg>[IMPROVEMENT] enhance functionality of locale util<commit_after>/*
 * Copyright (c) 2012-2015 S-Core Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * This file is supported of locale API
 *
 * @see support locale-sensitive languages.
 * @since: 2015.10.27
 * @author: minsung.jin
*/

define([
    'dojo/string'
], function (
    string
) {
    'use strict';

    var DEFAULT_ATTR_NAME = 'data-message';

    function _setMessage(resources, attrName, domNode) {
        var messageAttr = attrName || DEFAULT_ATTR_NAME;
        $('[' + messageAttr + ']', domNode).each(function () {
            var key = $(this).attr(messageAttr);
            $(this).text.call($(this), resources[key]);
        });
    }

    function LocaleUtil(resources) {
        this.resources = resources;
    }

    /**
     * Set messages to the DOM elements according to current locale option
     *
     * @param {{Element|Jquery}} [domNode] - Top level DOM node element to set message
     * @param {string} [attrName=data-message] - attribute name of any element that has messageKey  of
     *      the resource object as its value. It's optional parameter. And default value is 'data-message'.
     *
     * e.g. In HTML document, You can write like below.
     *

     *

     *              <button data-message="labelOk"></button>
     *

     *

     *      And you also have `labelOK` key in your resources object.
     *

     *          var resources = { ..., labelOk: 'OK', ...};
     *

     *      You can transit button's label as doing this.
     *

     *          var localeUtil = new LocaleUtil(resources);
     *          localeUtil.convertMessage(document.getElementById('container'), 'data-message');
     *

     */
    LocaleUtil.prototype.convertMessage = function (domNode, attrName) {
        if (typeof domNode === 'string') {
            domNode = document.getElementById(domNode);
        }
        _setMessage(this.resources, attrName, domNode);
    };

    /**
     * Get formatted message
     *
     * @param {string} messageKey - key name of the resource object
     * @param {object} [formatValues] - variables for formatting. If it is not set, it will raw message.
     * @returns {string} - formatted message
     */
    LocaleUtil.prototype.formatMessage = function (messageKey, formatValues) {
        var message = this.resources[messageKey] || '';
        return (formatValues) ? string.substitute(message, formatValues) : message;
    };

    /**
     * Original method
     * @param resources
     * @param attrName
     */
    LocaleUtil.convertMessage = function (resources, attrName, domNode) {
        _setMessage(resources, attrName, domNode);
    };

    return LocaleUtil;
});<|endoftext|>


<commit_before>import React from 'react'
import PropTypes from 'prop-types'
import mapboxgl from 'mapbox-gl'
import bbox from '@turf/bbox'
import flip from '@turf/flip'

import mapStyle from 'mapbox-gl/dist/mapbox-gl.css'

import {isBboxFlipped, flipBbox} from '../../lib/geo/bbox'

import Feature from './feature'

class CenteredMap extends React.Component {
  static propTypes = {
    data: PropTypes.object.isRequired,
    extent: PropTypes.object,
    frozen: PropTypes.bool
  }

  static defaultProps = {
    extent: null,
    frozen: false
  }

  state = {
    highlight: null
  }

  constructor(props) {
    super(props)

    this.bbox = bbox(props.data)
    if (props.extent) {
      if (isBboxFlipped(this.bbox, bbox(props.extent))) {
        // Even though we should never mutate a component’s props,
        // flipped coordinates should also never appear.
        // We’re mutating the `data` prop here (and it is faster).

        flip(props.data, {mutate: true})
        this.bbox = flipBbox(this.bbox)
      }
    }

    this.handlers = []

    if (!props.frozen) {
      for (const layer of ['point', 'polygon-fill', 'line']) {
        this.handlers.push({
          event: 'mousemove',
          layer,
          handler: this.onMouseMove.bind(this, layer)
        }, {
          event: 'mouseleave',
          layer,
          handler: this.onMouseLeave.bind(this, layer)
        })
      }
    }
  }

  componentDidMount() {
    const {frozen} = this.props

    this.map = new mapboxgl.Map({
      container: this.mapContainer,
      style: 'https://openmaptiles.geo.data.gouv.fr/styles/osm-bright/style.json',
      interactive: !frozen
    })

    this.map.fitBounds(this.bbox, {
      padding: 30,
      linear: true
    })

    this.map.once('load', this.onLoad)

    for (const {event, layer, handler} of this.handlers) {
      this.map.on(event, layer, handler)
    }
  }

  componentWillUnmount() {
    const {map} = this

    for (const {event, layer, handler} of this.handlers) {
      map.off(event, layer, handler)
    }
  }

  onLoad = () => {
    const {map} = this
    const {data} = this.props

    map.addSource('data', {
      type: 'geojson',
      data
    })

    map.addSource('hover', {
      type: 'geojson',
      data: {
        type: 'FeatureCollection',
        features: []
      }
    })

    map.addLayer({
      id: 'point',
      type: 'circle',
      source: 'data',
      paint: {
        'circle-radius': 5,
        'circle-color': '#3099df',
        'circle-opacity': 0.6
      },
      filter: ['==', '$type', 'Point']
    })

    map.addLayer({
      id: 'point-hover',
      type: 'circle',
      source: 'hover',
      paint: {
        'circle-radius': 5,
        'circle-color': '#2c3e50',
        'circle-opacity': 0.8
      },
      filter: ['==', '$type', 'Point']
    })

    map.addLayer({
      id: 'polygon-fill',
      type: 'fill',
      source: 'data',
      paint: {
        'fill-color': '#3099df',
        'fill-opacity': 0.3
      },
      filter: ['==', '$type', 'Polygon']
    })

    map.addLayer({
      id: 'polygon-fill-hover',
      type: 'fill',
      source: 'hover',
      paint: {
        'fill-color': '#2c3e50',
        'fill-opacity': 0.3
      },
      filter: ['==', '$type', 'Polygon']
    })

    map.addLayer({
      id: 'polygon-outline',
      type: 'line',
      source: 'data',
      paint: {
        'line-color': '#4790E5',
        'line-width': 2
      },
      filter: ['==', '$type', 'Polygon']
    })

    map.addLayer({
      id: 'line',
      type: 'line',
      source: 'data',
      paint: {
        'line-color': '#3099df',
        'line-width': 5,
        'line-opacity': 0.8
      },
      filter: ['==', '$type', 'LineString']
    })

    map.addLayer({
      id: 'line-hover',
      type: 'line',
      source: 'hover',
      paint: {
        'line-color': '#2c3e50',
        'line-width': 5,
        'line-opacity': 0.8
      },
      filter: ['==', '$type', 'LineString']
    })
  }

  onMouseMove = (layer, event) => {
    const {map} = this
    const canvas = map.getCanvas()
    canvas.style.cursor = 'pointer'

    const [feature] = event.features

    map.getSource('hover').setData({
      type: 'FeatureCollection',
      features: [feature]
    })

    this.setState({
      highlight: {
        feature,
        count: event.features.length
      }
    })
  }

  onMouseLeave = () => {
    const {map} = this
    const canvas = map.getCanvas()
    canvas.style.cursor = ''

    map.getSource('hover').setData({
      type: 'FeatureCollection',
      features: []
    })
  }

  render() {
    const {highlight} = this.state

    return (

 {
          this.mapContainer = el
        }} className='container' />

        {highlight && (

            <Feature feature={highlight.feature} otherFeaturesCount={highlight.count - 1} />

        )}

    )
  }
}

export default CenteredMap
<commit_msg>Make sure fitBounds duration is 0<commit_after>import React from 'react'
import PropTypes from 'prop-types'
import mapboxgl from 'mapbox-gl'
import bbox from '@turf/bbox'
import flip from '@turf/flip'

import mapStyle from 'mapbox-gl/dist/mapbox-gl.css'

import {isBboxFlipped, flipBbox} from '../../lib/geo/bbox'

import Feature from './feature'

class CenteredMap extends React.Component {
  static propTypes = {
    data: PropTypes.object.isRequired,
    extent: PropTypes.object,
    frozen: PropTypes.bool
  }

  static defaultProps = {
    extent: null,
    frozen: false
  }

  state = {
    highlight: null
  }

  constructor(props) {
    super(props)

    this.bbox = bbox(props.data)
    if (props.extent) {
      if (isBboxFlipped(this.bbox, bbox(props.extent))) {
        // Even though we should never mutate a component’s props,
        // flipped coordinates should also never appear.
        // We’re mutating the `data` prop here (and it is faster).

        flip(props.data, {mutate: true})
        this.bbox = flipBbox(this.bbox)
      }
    }

    this.handlers = []

    if (!props.frozen) {
      for (const layer of ['point', 'polygon-fill', 'line']) {
        this.handlers.push({
          event: 'mousemove',
          layer,
          handler: this.onMouseMove.bind(this, layer)
        }, {
          event: 'mouseleave',
          layer,
          handler: this.onMouseLeave.bind(this, layer)
        })
      }
    }
  }

  componentDidMount() {
    const {frozen} = this.props

    this.map = new mapboxgl.Map({
      container: this.mapContainer,
      style: 'https://openmaptiles.geo.data.gouv.fr/styles/osm-bright/style.json',
      interactive: !frozen
    })

    this.map.once('load', this.onLoad)

    this.map.fitBounds(this.bbox, {
      padding: 30,
      linear: true,
      duration: 0
    })

    for (const {event, layer, handler} of this.handlers) {
      this.map.on(event, layer, handler)
    }
  }

  componentWillUnmount() {
    const {map} = this

    for (const {event, layer, handler} of this.handlers) {
      map.off(event, layer, handler)
    }
  }

  onLoad = () => {
    const {map} = this
    const {data} = this.props

    map.addSource('data', {
      type: 'geojson',
      data
    })

    map.addSource('hover', {
      type: 'geojson',
      data: {
        type: 'FeatureCollection',
        features: []
      }
    })

    map.addLayer({
      id: 'point',
      type: 'circle',
      source: 'data',
      paint: {
        'circle-radius': 5,
        'circle-color': '#3099df',
        'circle-opacity': 0.6
      },
      filter: ['==', '$type', 'Point']
    })

    map.addLayer({
      id: 'point-hover',
      type: 'circle',
      source: 'hover',
      paint: {
        'circle-radius': 5,
        'circle-color': '#2c3e50',
        'circle-opacity': 0.8
      },
      filter: ['==', '$type', 'Point']
    })

    map.addLayer({
      id: 'polygon-fill',
      type: 'fill',
      source: 'data',
      paint: {
        'fill-color': '#3099df',
        'fill-opacity': 0.3
      },
      filter: ['==', '$type', 'Polygon']
    })

    map.addLayer({
      id: 'polygon-fill-hover',
      type: 'fill',
      source: 'hover',
      paint: {
        'fill-color': '#2c3e50',
        'fill-opacity': 0.3
      },
      filter: ['==', '$type', 'Polygon']
    })

    map.addLayer({
      id: 'polygon-outline',
      type: 'line',
      source: 'data',
      paint: {
        'line-color': '#4790E5',
        'line-width': 2
      },
      filter: ['==', '$type', 'Polygon']
    })

    map.addLayer({
      id: 'line',
      type: 'line',
      source: 'data',
      paint: {
        'line-color': '#3099df',
        'line-width': 5,
        'line-opacity': 0.8
      },
      filter: ['==', '$type', 'LineString']
    })

    map.addLayer({
      id: 'line-hover',
      type: 'line',
      source: 'hover',
      paint: {
        'line-color': '#2c3e50',
        'line-width': 5,
        'line-opacity': 0.8
      },
      filter: ['==', '$type', 'LineString']
    })
  }

  onMouseMove = (layer, event) => {
    const {map} = this
    const canvas = map.getCanvas()
    canvas.style.cursor = 'pointer'

    const [feature] = event.features

    map.getSource('hover').setData({
      type: 'FeatureCollection',
      features: [feature]
    })

    this.setState({
      highlight: {
        feature,
        count: event.features.length
      }
    })
  }

  onMouseLeave = () => {
    const {map} = this
    const canvas = map.getCanvas()
    canvas.style.cursor = ''

    map.getSource('hover').setData({
      type: 'FeatureCollection',
      features: []
    })
  }

  render() {
    const {highlight} = this.state

    return (

 {
          this.mapContainer = el
        }} className='container' />

        {highlight && (

            <Feature feature={highlight.feature} otherFeaturesCount={highlight.count - 1} />

        )}

    )
  }
}

export default CenteredMap
<|endoftext|>