<commit_before>/*
 * Axelor Business Solutions
 *
 * Copyright (C) 2005-2015 Axelor (<http://axelor.com>).
 *
 * This program is free software: you can redistribute it and/or  modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
(function() {

"use strict";

var ui = angular.module('axelor.ui');

function BaseCardsCtrl(type, $scope, $element) {

	DSViewCtrl(type, $scope, $element);

	$scope.getRouteOptions = function() {
		return {
			mode: type,
			args: []
		};
	};

	$scope.setRouteOptions = function(options) {
		if (!$scope.isNested) {
			$scope.updateRoute();
		}
	};

	var ds = $scope._dataSource;
	var initialized = false;

	$scope.onShow = function (viewPromise) {

		if (initialized) {
			return;
		}

		initialized = true;

		viewPromise.then(function (meta) {
			$scope.parse(meta.fields, meta.view);
		});
	};

	$scope.parse = function (fields, view) {

	};

	$scope.onRefresh = function () {
		$scope.filter({});
	};

	function update(records) {
		$scope.records = records;
	}

	$scope.filter = function(options) {
		var view = $scope.schema;
		var opts = {
			fields: _.pluck($scope.fields, 'name'),
		};

		if (options.criteria || options._domains) {
			opts.filter = options;
		}
		if (options.archived !== undefined) {
			opts.archived = options.archived;
		}
		if (view.orderBy) {
			opts.sortBy = view.orderBy.split(',');
		}

		return ds.search(opts).success(update);
	};

	$scope.pagerText = function() {
		var page = ds._page;
		if (page && page.from !== undefined) {
			if (page.total == 0) return null;
			return _t("{0} to {1} of {2}", page.from + 1, page.to, page.total);
		}
	};

	$scope.onNext = function() {
		var fields = _.pluck($scope.fields, 'name');
		return ds.next(fields).success(update);
	};

	$scope.onPrev = function() {
		var fields = _.pluck($scope.fields, 'name');
		return ds.prev(fields).success(update);
	};
}

ui.controller("CardsCtrl", ['$scope', '$element', function CardsCtrl($scope, $element) {

	BaseCardsCtrl('cards', $scope, $element);

	$scope.parse = function (fields, view) {
		$scope.onRefresh();
	}
}]);

ui.controller("KanbanCtrl", ['$scope', '$element', function KanbanCtrl($scope, $element) {

	BaseCardsCtrl('kanban', $scope, $element);

	$scope.parse = function (fields, view) {
		var columnBy = fields[view.columnBy] || {};
		var columns = _.map(columnBy.selectionList, function (item) {
			return item;
		});

		columns = _.first(columns, 6);

		var first = _.first(columns);
		if (view.onNew) {
			first.canCreate = true;
		}

		var sequenceBy = fields[view.sequenceBy] || {};
		if (["integer", "long"].indexOf(sequenceBy.type) === -1) {
			throw new Error("Invalid sequenceBy field in view: " + view.name);
		}

		$scope.columns = columns;
		$scope.colSpan = "span" + (12 / columns.length);
	};

	$scope.move = function (record, to, next, prev) {

		var view = $scope.schema;
		var rec = _.pick(record, "id", "version", view.sequenceBy);
		var ds = $scope._dataSource._new($scope._model);

		// update columnBy
		rec[view.columnBy] = to;

		// update sequenceBy
		var all = _.compact([prev, rec, next]);
		var offset = _.min(_.pluck(all, view.sequenceBy)) || 0;

		_.each(all, function (item, i) {
			item[view.sequenceBy] = offset + i;
		});

		return ds.saveAll(all).success(function (records) {
			_.each(all, function (item) {
				_.extend(item, ds.get(item.id));
			});
			record.version = rec.version;
		});
	};

	$scope.onNew = function () {
		$scope.switchTo('form', function (formScope) {
			formScope.edit(null);
			formScope.setEditable();
			formScope.$broadcast("on:new");
		});
	};

	$scope.onRefresh = function () {
		$scope.$broadcast("on:refresh");
	};

	$scope.filter = function(searchFilter) {
		var options = {};
		if (searchFilter.criteria || searchFilter._domains) {
			options = {
				filter: searchFilter
			};
			if (searchFilter.archived !== undefined) {
				options.archived = searchFilter.archived;
			}
			$scope.$broadcast("on:filter", options);
		}
	};
}]);

ui.directive('uiKanban', function () {

	return function (scope, element, attrs) {

		function makeSortables() {
			element.find(".kanban-card-list").sortable({
				connectWith: ".kanban-card-list",
				items: ".kanban-card",
				tolerance: "pointer",
				stop: function (event, ui) {
					var item = ui.item;
					var column = item.parent().scope().column;
					var next = item.next().scope();
					var prev = item.prev().scope();

					if (next) next = next.record;
					if (prev) prev = prev.record;

					var source = $(this).scope();
					var target = item.parent().scope();
					var record = item.scope().record;

					if (source === target && item.index() === source.records.indexOf(record)) {
						return;
					}

					source.reorder();
					if (target !== source) {
						target.reorder();
					}

					scope.move(record, column.value, next, prev);
					scope.applyLater();
				}
			});
		}

		var unwatch = scope.$watch("columns", function (cols) {
			if (cols) {
				unwatch();
				unwatch = null;
				setTimeout(makeSortables);
			}
		});
	};
});

ui.directive('uiKanbanColumn', ["ActionService", function (ActionService) {

	return {
		scope: true,
		link: function (scope, element, attrs) {

			var ds = scope._dataSource._new(scope._model);
			var view = scope.schema;

			ds._context = _.extend({}, scope._dataSource._context);
			ds._context[view.columnBy] = scope.column.value;
			ds._page.limit = view.limit || 20;

			var domain = "self." + view.columnBy + " = :" + view.columnBy;
			ds._domain = ds._domain ? ds._domain + " AND " + domain : domain;

			scope.records = [];

			function fetch(options) {
				var opts = _.extend({
					offset: 0,
					sortBy: [view.sequenceBy]
				}, options);
				ds.search(opts).success(function (records) {
					scope.records = scope.records.concat(records);
				});
			}

			scope.hasMore = function () {
				var page = ds._page;
				var next = page.from + page.limit;
				return next < page.total;
			};

			scope.onMore = function () {
				var page = ds._page;
				var next = page.from + page.limit;
				if (next < page.total) {
					return fetch({
						offset: next
					});
				}
			};

			scope.reorder = function () {
				var items = [];
				element.find("li.kanban-card").each(function () {
					items.push($(this).scope().record);
				});
				scope.records = items;
			};

			var onNew = null;

			scope.getContext = function () {
				var ctx = _.extend({}, scope._context);
				ctx._value = scope.newItem;
				return ctx;
			};

			scope.newItem = null;
			scope.onCreate = function () {

				var rec = scope.record = {};
				var view = scope.schema;

				rec[view.columnBy] = scope.column.value;

				if (onNew === null) {
					onNew = ActionService.handler(scope, element, {
						action: view.onNew
					});
				}

				var ds = scope._dataSource;
				var promise = onNew.handle();
				promise.then(function () {
					ds.save(scope.record).success(function (rec) {
						scope.newItem = null;
						scope.records.unshift(rec);
					});
				});
			};

			scope.onEdit = function (record) {
				scope.switchTo('form', function (formScope) {
					if (formScope.canEdit()) {
						formScope.edit(record);
						formScope.setEditable();
					}
				});
			};

			scope.onDelete = function (record) {
				axelor.dialogs.confirm(_t("Do you really want to delete the selected record?"),
				function(confirmed) {
					if (!confirmed) {
						return;
					}
					ds.removeAll([record]).success(function(records, page) {
						var index = scope.records.indexOf(record);
						scope.records.splice(index, 1);
					});
				});
			};

			scope.$on("on:refresh", function (e) {
				scope.newItem = null;
				scope.records.length = 0;
				fetch();
			});

			scope.$on("on:filter", function (e, options) {
				scope.newItem = null;
				scope.records.length = 0;
				return fetch(options);
			});

			fetch();
		}
	};
}]);

ui.directive('uiCards', function () {

	return function (scope, element, attrs) {

		scope.onEdit = function (record, readonly) {
			scope.switchTo('form', function (formScope) {
				if (formScope.canEdit()) {
					formScope.edit(record);
					if (!readonly) {
						formScope.setEditable();
					}
				}
			});
		};

		scope.onDelete = function (record) {
			axelor.dialogs.confirm(_t("Do you really want to delete the selected record?"),
			function(confirmed) {
				if (!confirmed) {
					return;
				}
				ds.removeAll([record]).success(function(records, page) {
					var index = scope.records.indexOf(record);
					scope.records.splice(index, 1);
				});
			});
		};

		element.on("click", ".kanban-card", function (e) {
			if ($(e.target).parents(".kanban-card-menu").size()) {
				return;
			}
			var elem = $(this);
			var record = elem.scope().record;
			scope.onEdit(record, true);
			scope.applyLater();
		});
	};
});

ui.directive('uiCard', ["$parse", "$compile", function ($parse, $compile) {

	return {
		scope: true,
		link: function (scope, element, attrs) {

			var record = scope.record;
			var evalScope = scope.$new(true);

			if (!record.$processed) {
				element.hide();
			}

			function process(record) {
				record.$processed = true;
				for (var name in record) {
					if (!record.hasOwnProperty(name) || name.indexOf('.') === -1) {
						continue;
					}
					var nested = record;
					var names = name.split('.');
					var head = _.first(names, names.length - 1);
					var last = _.last(names);
					head.forEach(function (n) {
						nested = nested[n] || (nested[n] = {});
					});
					nested[last] = record[name];
				}
				return record;
			}

			_.extend(evalScope, process(scope.record));

			evalScope.$image = function (fieldName, imageName) {
				var rec = scope.record;
				if (fieldName === null && imageName) {
					return "ws/rest/" + scope._model + "/" + rec.id + "/" + imageName + "/download?image=true";
				}
				var field = scope.fields[fieldName];
				if (field && field.target && rec[fieldName]) {
					var val = rec[fieldName];
					return "ws/rest/" + field.target + "/" + val.id + "/" + imageName + "/download?image=true";
				}
				return "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
			};

			var template = (scope.schema.template || "").trim();
			if (template.indexOf('<') !== 0) {
				template = "" + template + "";
			}

			scope.hilite = null;
			scope.content = $compile(template)(evalScope);

			var hilites = scope.schema.hilites || [];
			for (var i = 0; i < hilites.length; i++) {
				var hilite = hilites[i];
				var expr = $parse(hilite.condition);
				if (expr(scope.record)) {
					scope.hilite = hilite;
					break;
				}
			}

			if (scope.schema.cardWidth) {
				element.parent().css("width", scope.schema.cardWidth);
			}

			element.fadeIn("slow");
		}
	};
}]);

})();
<commit_msg>Fix image caching issue in kanban cards<commit_after>/*
 * Axelor Business Solutions
 *
 * Copyright (C) 2005-2015 Axelor (<http://axelor.com>).
 *
 * This program is free software: you can redistribute it and/or  modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
(function() {

"use strict";

var ui = angular.module('axelor.ui');

function BaseCardsCtrl(type, $scope, $element) {

	DSViewCtrl(type, $scope, $element);

	$scope.getRouteOptions = function() {
		return {
			mode: type,
			args: []
		};
	};

	$scope.setRouteOptions = function(options) {
		if (!$scope.isNested) {
			$scope.updateRoute();
		}
	};

	var ds = $scope._dataSource;
	var initialized = false;

	$scope.onShow = function (viewPromise) {

		if (initialized) {
			return;
		}

		initialized = true;

		viewPromise.then(function (meta) {
			$scope.parse(meta.fields, meta.view);
		});
	};

	$scope.parse = function (fields, view) {

	};

	$scope.onRefresh = function () {
		$scope.filter({});
	};

	function update(records) {
		$scope.records = records;
	}

	$scope.filter = function(options) {
		var view = $scope.schema;
		var opts = {
			fields: _.pluck($scope.fields, 'name'),
		};

		if (options.criteria || options._domains) {
			opts.filter = options;
		}
		if (options.archived !== undefined) {
			opts.archived = options.archived;
		}
		if (view.orderBy) {
			opts.sortBy = view.orderBy.split(',');
		}

		return ds.search(opts).success(update);
	};

	$scope.pagerText = function() {
		var page = ds._page;
		if (page && page.from !== undefined) {
			if (page.total == 0) return null;
			return _t("{0} to {1} of {2}", page.from + 1, page.to, page.total);
		}
	};

	$scope.onNext = function() {
		var fields = _.pluck($scope.fields, 'name');
		return ds.next(fields).success(update);
	};

	$scope.onPrev = function() {
		var fields = _.pluck($scope.fields, 'name');
		return ds.prev(fields).success(update);
	};
}

ui.controller("CardsCtrl", ['$scope', '$element', function CardsCtrl($scope, $element) {

	BaseCardsCtrl('cards', $scope, $element);

	$scope.parse = function (fields, view) {
		$scope.onRefresh();
	}
}]);

ui.controller("KanbanCtrl", ['$scope', '$element', function KanbanCtrl($scope, $element) {

	BaseCardsCtrl('kanban', $scope, $element);

	$scope.parse = function (fields, view) {
		var columnBy = fields[view.columnBy] || {};
		var columns = _.map(columnBy.selectionList, function (item) {
			return item;
		});

		columns = _.first(columns, 6);

		var first = _.first(columns);
		if (view.onNew) {
			first.canCreate = true;
		}

		var sequenceBy = fields[view.sequenceBy] || {};
		if (["integer", "long"].indexOf(sequenceBy.type) === -1) {
			throw new Error("Invalid sequenceBy field in view: " + view.name);
		}

		$scope.columns = columns;
		$scope.colSpan = "span" + (12 / columns.length);
	};

	$scope.move = function (record, to, next, prev) {

		var view = $scope.schema;
		var rec = _.pick(record, "id", "version", view.sequenceBy);
		var ds = $scope._dataSource._new($scope._model);

		// update columnBy
		rec[view.columnBy] = to;

		// update sequenceBy
		var all = _.compact([prev, rec, next]);
		var offset = _.min(_.pluck(all, view.sequenceBy)) || 0;

		_.each(all, function (item, i) {
			item[view.sequenceBy] = offset + i;
		});

		return ds.saveAll(all).success(function (records) {
			_.each(all, function (item) {
				_.extend(item, ds.get(item.id));
			});
			record.version = rec.version;
		});
	};

	$scope.onNew = function () {
		$scope.switchTo('form', function (formScope) {
			formScope.edit(null);
			formScope.setEditable();
			formScope.$broadcast("on:new");
		});
	};

	$scope.onRefresh = function () {
		$scope.$broadcast("on:refresh");
	};

	$scope.filter = function(searchFilter) {
		var options = {};
		if (searchFilter.criteria || searchFilter._domains) {
			options = {
				filter: searchFilter
			};
			if (searchFilter.archived !== undefined) {
				options.archived = searchFilter.archived;
			}
			$scope.$broadcast("on:filter", options);
		}
	};
}]);

ui.directive('uiKanban', function () {

	return function (scope, element, attrs) {

		function makeSortables() {
			element.find(".kanban-card-list").sortable({
				connectWith: ".kanban-card-list",
				items: ".kanban-card",
				tolerance: "pointer",
				stop: function (event, ui) {
					var item = ui.item;
					var column = item.parent().scope().column;
					var next = item.next().scope();
					var prev = item.prev().scope();

					if (next) next = next.record;
					if (prev) prev = prev.record;

					var source = $(this).scope();
					var target = item.parent().scope();
					var record = item.scope().record;

					if (source === target && item.index() === source.records.indexOf(record)) {
						return;
					}

					source.reorder();
					if (target !== source) {
						target.reorder();
					}

					scope.move(record, column.value, next, prev);
					scope.applyLater();
				}
			});
		}

		var unwatch = scope.$watch("columns", function (cols) {
			if (cols) {
				unwatch();
				unwatch = null;
				setTimeout(makeSortables);
			}
		});
	};
});

ui.directive('uiKanbanColumn', ["ActionService", function (ActionService) {

	return {
		scope: true,
		link: function (scope, element, attrs) {

			var ds = scope._dataSource._new(scope._model);
			var view = scope.schema;

			ds._context = _.extend({}, scope._dataSource._context);
			ds._context[view.columnBy] = scope.column.value;
			ds._page.limit = view.limit || 20;

			var domain = "self." + view.columnBy + " = :" + view.columnBy;
			ds._domain = ds._domain ? ds._domain + " AND " + domain : domain;

			scope.records = [];

			function fetch(options) {
				var opts = _.extend({
					offset: 0,
					sortBy: [view.sequenceBy]
				}, options);
				ds.search(opts).success(function (records) {
					scope.records = scope.records.concat(records);
				});
			}

			scope.hasMore = function () {
				var page = ds._page;
				var next = page.from + page.limit;
				return next < page.total;
			};

			scope.onMore = function () {
				var page = ds._page;
				var next = page.from + page.limit;
				if (next < page.total) {
					return fetch({
						offset: next
					});
				}
			};

			scope.reorder = function () {
				var items = [];
				element.find("li.kanban-card").each(function () {
					items.push($(this).scope().record);
				});
				scope.records = items;
			};

			var onNew = null;

			scope.getContext = function () {
				var ctx = _.extend({}, scope._context);
				ctx._value = scope.newItem;
				return ctx;
			};

			scope.newItem = null;
			scope.onCreate = function () {

				var rec = scope.record = {};
				var view = scope.schema;

				rec[view.columnBy] = scope.column.value;

				if (onNew === null) {
					onNew = ActionService.handler(scope, element, {
						action: view.onNew
					});
				}

				var ds = scope._dataSource;
				var promise = onNew.handle();
				promise.then(function () {
					ds.save(scope.record).success(function (rec) {
						scope.newItem = null;
						scope.records.unshift(rec);
					});
				});
			};

			scope.onEdit = function (record) {
				scope.switchTo('form', function (formScope) {
					if (formScope.canEdit()) {
						formScope.edit(record);
						formScope.setEditable();
					}
				});
			};

			scope.onDelete = function (record) {
				axelor.dialogs.confirm(_t("Do you really want to delete the selected record?"),
				function(confirmed) {
					if (!confirmed) {
						return;
					}
					ds.removeAll([record]).success(function(records, page) {
						var index = scope.records.indexOf(record);
						scope.records.splice(index, 1);
					});
				});
			};

			scope.$on("on:refresh", function (e) {
				scope.newItem = null;
				scope.records.length = 0;
				fetch();
			});

			scope.$on("on:filter", function (e, options) {
				scope.newItem = null;
				scope.records.length = 0;
				return fetch(options);
			});

			fetch();
		}
	};
}]);

ui.directive('uiCards', function () {

	return function (scope, element, attrs) {

		scope.onEdit = function (record, readonly) {
			scope.switchTo('form', function (formScope) {
				if (formScope.canEdit()) {
					formScope.edit(record);
					if (!readonly) {
						formScope.setEditable();
					}
				}
			});
		};

		scope.onDelete = function (record) {
			axelor.dialogs.confirm(_t("Do you really want to delete the selected record?"),
			function(confirmed) {
				if (!confirmed) {
					return;
				}
				ds.removeAll([record]).success(function(records, page) {
					var index = scope.records.indexOf(record);
					scope.records.splice(index, 1);
				});
			});
		};

		element.on("click", ".kanban-card", function (e) {
			if ($(e.target).parents(".kanban-card-menu").size()) {
				return;
			}
			var elem = $(this);
			var record = elem.scope().record;
			scope.onEdit(record, true);
			scope.applyLater();
		});
	};
});

ui.directive('uiCard', ["$parse", "$compile", function ($parse, $compile) {

	return {
		scope: true,
		link: function (scope, element, attrs) {

			var record = scope.record;
			var evalScope = scope.$new(true);

			if (!record.$processed) {
				element.hide();
			}

			function process(record) {
				record.$processed = true;
				for (var name in record) {
					if (!record.hasOwnProperty(name) || name.indexOf('.') === -1) {
						continue;
					}
					var nested = record;
					var names = name.split('.');
					var head = _.first(names, names.length - 1);
					var last = _.last(names);
					head.forEach(function (n) {
						nested = nested[n] || (nested[n] = {});
					});
					nested[last] = record[name];
				}
				return record;
			}

			_.extend(evalScope, process(scope.record));

			evalScope.$image = function (fieldName, imageName) {
				var rec = scope.record;
				var v = rec.version || rec.$version || (new Date()).getTime();
				if (fieldName === null && imageName) {
					return "ws/rest/" + scope._model + "/" + rec.id + "/" + imageName + "/download?image=true&v=" + v;
				}
				var field = scope.fields[fieldName];
				if (field && field.target && rec[fieldName]) {
					var val = rec[fieldName];
					return "ws/rest/" + field.target + "/" + val.id + "/" + imageName + "/download?image=true&v=" + v;
				}
				return "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
			};

			var template = (scope.schema.template || "").trim();
			if (template.indexOf('<') !== 0) {
				template = "" + template + "";
			}

			scope.hilite = null;
			scope.content = $compile(template)(evalScope);

			var hilites = scope.schema.hilites || [];
			for (var i = 0; i < hilites.length; i++) {
				var hilite = hilites[i];
				var expr = $parse(hilite.condition);
				if (expr(scope.record)) {
					scope.hilite = hilite;
					break;
				}
			}

			if (scope.schema.cardWidth) {
				element.parent().css("width", scope.schema.cardWidth);
			}

			element.fadeIn("slow");
		}
	};
}]);

})();
<|endoftext|>


<commit_before>import * as Babel from 'babel-standalone'
import _ from 'lodash'
import PropTypes from 'prop-types'
import React, { Component, createElement, isValidElement } from 'react'
import { withRouter } from 'react-router'
import { renderToStaticMarkup } from 'react-dom/server'
import { html } from 'js-beautify'
import copyToClipboard from 'copy-to-clipboard'

import { exampleContext, repoURL } from 'docs/app/utils'
import { Divider, Grid, Icon, Header, Menu, Popup } from 'src'
import Editor from 'docs/app/Components/Editor/Editor'
import { scrollToAnchor } from 'docs/app/utils'

const babelConfig = {
  presets: ['es2015', 'react', 'stage-1'],
}

const titleStyle = {
  margin: 0,
}
const descriptionStyle = {
  maxWidth: '50rem',
}

const headerColumnStyle = {
  // provide room for absolutely positions toggle code icons
  minHeight: '4em',
  paddingRight: '7em',
}

const childrenStyle = {
  paddingTop: 0,
  maxWidth: '50rem',
}

const errorStyle = {
  padding: '1em',
  fontSize: '0.9rem',
  color: '#a33',
  background: '#fff2f2',
}

const toolTipStyle = { width: 100, textAlign: 'center', padding: '0.5em' }
const ToolTip = ({ children, content }) => (
  <Popup
    inverted
    mouseEnterDelay={800}
    position='top center'
    size='tiny'
    style={toolTipStyle}
    trigger={children}
    content={content}
  />
)
ToolTip.propTypes = {
  children: PropTypes.node,
  content: PropTypes.node,
}

/**
 * Renders a `component` and the raw `code` that produced it.
 * Allows toggling the the raw `code` code block.
 */
class ComponentExample extends Component {
  static propTypes = {
    children: PropTypes.node,
    description: PropTypes.node,
    examplePath: PropTypes.string.isRequired,
    history: PropTypes.object.isRequired,
    location: PropTypes.object.isRequired,
    match: PropTypes.object.isRequired,
    title: PropTypes.node,
  }

  componentWillMount() {
    const { examplePath } = this.props
    const sourceCode = this.getOriginalSourceCode()

    this.anchorName = _.kebabCase(_.last(examplePath.split('/')))

    // show code for direct links to examples
    const showCode = this.anchorName === location.hash.replace('#', '')
    const exampleElement = this.renderOriginalExample()
    const markup = renderToStaticMarkup(exampleElement)

    this.setState({
      exampleElement,
      showCode,
      sourceCode,
      markup,
    })
  }

  setHashAndScroll = () => {
    const { history } = this.props
    history.replace(location.pathname + '#' + this.anchorName)
    scrollToAnchor()
  }

  removeHash = () => {
    const { history } = this.props
    history.replace(location.pathname)
  }

  handleDirectLinkClick = (e) => {
    e.preventDefault()
    this.setHashAndScroll()

    copyToClipboard(location.href)
    this.setState({ copiedDirectLink: true })

    setTimeout(() => this.setState({ copiedDirectLink: false }), 1000)
  }

  handleShowCodeClick = (e) => {
    e.preventDefault()

    const { showCode } = this.state
    this.setState({ showCode: !showCode })

    if (!showCode) this.setHashAndScroll()
    else this.removeHash()
  }

  handleShowHTMLClick = (e) => {
    e.preventDefault()

    const { showHTML } = this.state
    this.setState({ showHTML: !showHTML })

    if (!showHTML) this.setHashAndScroll()
    else this.removeHash()
  }

  copyJSX = () => {
    copyToClipboard(this.state.sourceCode)
    this.setState({ copiedCode: true })
    setTimeout(() => this.setState({ copiedCode: false }), 1000)
  }

  resetJSX = () => {
    const { sourceCode } = this.state
    const original = this.getOriginalSourceCode()
    if (sourceCode !== original && confirm('Lose your changes?')) { // eslint-disable-line no-alert
      this.setState({ sourceCode: original })
      this.renderSourceCode()
    }
  }

  getOriginalSourceCode = () => {
    const { examplePath } = this.props

    if (!this.sourceCode) this.sourceCode = require(`!raw-loader!../../Examples/${examplePath}`)

    return this.sourceCode
  }

  getKebabExamplePath = () => {
    if (!this.kebabExamplePath) this.kebabExamplePath = _.kebabCase(this.props.examplePath)

    return this.kebabExamplePath
  }

  renderError = _.debounce((error) => {
    this.setState({ error })
  }, 800)

  renderOriginalExample = () => {
    const { examplePath } = this.props
    return createElement(exampleContext(`./${examplePath}.js`).default)
  }

  renderSourceCode = _.debounce(() => {
    const { examplePath } = this.props
    const { sourceCode } = this.state
    // Heads Up!
    //
    // These are used in the code editor scope when rewriting imports to const statements
    // We use require() to preserve variable names
    // Webpack rewrites import names
    /* eslint-disable no-unused-vars */
    const FAKER = require('faker')
    const LODASH = require('lodash')
    const REACT = require('react')
    const SEMANTIC_UI_REACT = require('semantic-ui-react')
    let COMMON
    /* eslint-enable no-unused-vars */

    // Should use an AST transform here... oh well :/
    // Rewrite the example file into an IIFE that returns a component
    // which can be rendered in this ComponentExample's render() method

    // rewrite imports to const statements against the UPPERCASE module names
    const imports = _.get(/(^import[\s\S]*from[\s\S]*['"]\n)/.exec(sourceCode), '[1]', '')
      .replace(/[\s\n]+/g, ' ')         // normalize spaces and make one line
      .replace(/ import/g, '\nimport')  // one import per line
      .split('\n')                      // split lines
      .filter(Boolean)                  // remove empty lines
      .map(l => {                       // rewrite imports to const statements
        const [
          defaultImport,
          destructuredImports,
          _module,
        ] = _.tail(/import\s+([\w]+)?(?:\s*,\s*)?({[\s\w,]+})?\s+from\s+['"](?:.*\/)?([\w\-_]+)['"]/.exec(l))

        const module = _.snakeCase(_module).toUpperCase()

        if (module === 'COMMON') {
          const componentPath = examplePath.split(__PATH_SEP__).splice(0, 2).join(__PATH_SEP__)
          COMMON = require(`docs/app/Examples/${componentPath}/common`)
        }

        const constStatements = []
        if (defaultImport) constStatements.push(`const ${defaultImport} = ${module}`)
        if (destructuredImports) constStatements.push(`const ${destructuredImports} = ${module}`)
        constStatements.push('\n')

        return constStatements.join('\n')
      })
      .join('\n')

    // capture the default export so we can return it from the IIFE
    const defaultExport = _.get(/export\s+default\s+(?:class|function)?(?:\s+)?(\w+)/.exec(sourceCode), '[1]')

    // consider everything after the imports to be the body
    // remove `export` statements except `export default class|function`
    const body = _.get(/import[\s\S]*from.*\n([\s\S]*)/.exec(sourceCode), '[1]', '')
      .replace(/export\s+default\s+(?!class|function)\w+([\s\n]+)?/, '')  // remove `export default Foo` statements
      .replace(/export\s+default\s+/, '')                                 // remove `export default ...`

    const IIFE = `(function() {\n${imports}${body}return ${defaultExport}\n}())`

    try {
      const { code } = Babel.transform(IIFE, babelConfig)
      const Example = eval(code) // eslint-disable-line no-eval
      const exampleElement = _.isFunction(Example) ? <Example /> : Example

      if (!isValidElement(exampleElement)) {
        this.renderError('Default export is not a valid element. Type:' + {}.toString.call(exampleElement))
      } else {
        // immediately render a null error
        // but also ensure the last debounced error call is a null error
        const error = null
        this.renderError(error)
        this.setState({
          error,
          exampleElement,
          markup: renderToStaticMarkup(exampleElement),
        })
      }
    } catch (err) {
      this.renderError(err.message)
    }
  }, 100)

  handleChangeCode = (sourceCode) => {
    this.setState({ sourceCode })
    this.renderSourceCode()
  }

  setGitHubHrefs = () => {
    const { examplePath } = this.props

    if (this.ghEditHref && this.ghBugHref) return

    // get component name from file path:
    // elements/Button/Types/ButtonButtonExample
    const pathParts = examplePath.split(__PATH_SEP__)
    const componentName = pathParts[1]
    const filename = pathParts[pathParts.length - 1]

    this.ghEditHref = [
      `${repoURL}/edit/master/docs/app/Examples/${examplePath}.js`,
      `?message=docs(${filename}): your description`,
    ].join('')

    this.ghBugHref = [
      `${repoURL}/issues/new?`,
      _.map({
        title: `fix(${componentName}): your description`,
        body: [
          '**Steps to Reproduce**',
          '1. Do something',
          '2. Do something else.',
          '',
          '**Expected**',
          `The ${componentName} should do this`,
          '',
          '**Result**',
          `The ${componentName} does not do this`,
          '',
          '**Testcase**',
          `If the docs show the issue, use: ${window.location.href}`,
          'Otherwise, fork this to get started: http://codepen.io/levithomason/pen/ZpBaJX',
        ].join('\n'),
      }, (val, key) => `${key}=${encodeURIComponent(val)}`).join('&'),
    ].join('')
  }

  renderJSXControls = () => {
    const { copiedCode, error } = this.state

    this.setGitHubHrefs()

    const color = error ? 'red' : 'black'
    return (
      <Divider horizontal fitted>
        <Menu text>
          <Menu.Item
            active={copiedCode || !!error} // to show the color
            color={copiedCode ? 'green' : color}
            onClick={this.copyJSX}
            icon={!copiedCode && 'copy'}
            content={copiedCode ? 'Copied!' : 'Copy'}
          />
          <Menu.Item
            active={!!error} // to show the color
            color={color}
            icon='refresh'
            content='Reset'
            onClick={this.resetJSX}
          />
          <Menu.Item
            active={!!error} // to show the color
            color={color}
            icon='github'
            content='Edit'
            href={this.ghEditHref}
            target='_blank'
          />
          <Menu.Item
            active={!!error} // to show the color
            color={color}
            icon='bug'
            content='Issue'
            href={this.ghBugHref}
            target='_blank'
          />
        </Menu>
      </Divider>
    )
  }

  renderJSX = () => {
    const { error, showCode, sourceCode } = this.state
    if (!showCode) return

    const style = {}
    if (error) {
      style.boxShadow = `inset 0 0 0 1em ${errorStyle.background}`
    }

    return (

        {this.renderJSXControls()}
        <Editor
          id={`${this.getKebabExamplePath()}-jsx`}
          value={sourceCode}
          onChange={this.handleChangeCode}
        />
        {error && (

{error}

        )}

    )
  }

  renderHTML = () => {
    const { showHTML, markup } = this.state
    if (!showHTML) return

    // add new lines between almost all adjacent elements
    // moves inline elements to their own line
    const preFormattedHTML = markup
      .replace(/><(?!\/i|\/label|\/span|option)/g, '>\n<')

    const beautifiedHTML = html(preFormattedHTML, {
      indent_size: 2,
      indent_char: ' ',
      wrap_attributes: 'auto',
      wrap_attributes_indent_size: 2,
      end_with_newline: false,
    })

    return (

        <Divider horizontal>Rendered HTML</Divider>
        <Editor id={`${this.getKebabExamplePath()}-html`} mode='html' value={beautifiedHTML} readOnly />

    )
  }

  render() {
    const { children, description, title } = this.props
    const { copiedDirectLink, exampleElement, showCode, showHTML } = this.state
    const exampleStyle = {}

    if (showCode || showHTML || location.hash === `#${this.anchorName}`) {
      exampleStyle.boxShadow = '0 0 30px #ccc'
    }

    return (
      <Grid className='docs-example' style={exampleStyle} divided={showCode} columns='1' id={this.anchorName}>
        <Grid.Column style={headerColumnStyle}>
          {title && }
          {description &&
{description}
}
          <Menu compact text icon size='small' color='green' className='docs-example-menu'>
            <ToolTip content={copiedDirectLink ? ' Copied Link!' : 'Direct link'}>
              <Menu.Item href={`#${this.anchorName}`} onClick={this.handleDirectLinkClick}>
                <Icon size='large' color='grey' name='linkify' fitted />
              </Menu.Item>
            </ToolTip>
            <ToolTip content='Full Screen'>
              <Menu.Item href={`/maximize/${this.anchorName}`} target='_blank'>
                <Icon size='large' color='grey' name='window maximize' fitted />
              </Menu.Item>
            </ToolTip>
            <ToolTip content='Show HTML'>
              <Menu.Item active={showHTML} onClick={this.handleShowHTMLClick}>
                <Icon size='large' color={showHTML ? 'green' : 'grey'} name='html5' fitted />
              </Menu.Item>
            </ToolTip>
            <ToolTip content='Edit Code'>
              <Menu.Item active={showCode} onClick={this.handleShowCodeClick}>
                <Icon size='large' name='code' fitted />
              </Menu.Item>
            </ToolTip>
          </Menu>
        </Grid.Column>
        {children && (
          <Grid.Column style={childrenStyle}>
            {children}
          </Grid.Column>
        )}
        <Grid.Column className={`rendered-example ${this.getKebabExamplePath()}`}>
          {exampleElement}
        </Grid.Column>
        {this.renderJSX()}
        {this.renderHTML()}
      </Grid>
    )
  }
}

export default withRouter(ComponentExample)
<commit_msg>docs(ComponentExample): fix width in Firefox (#1916)<commit_after>import * as Babel from 'babel-standalone'
import _ from 'lodash'
import PropTypes from 'prop-types'
import React, { Component, createElement, isValidElement } from 'react'
import { withRouter } from 'react-router'
import { renderToStaticMarkup } from 'react-dom/server'
import { html } from 'js-beautify'
import copyToClipboard from 'copy-to-clipboard'

import { exampleContext, repoURL } from 'docs/app/utils'
import { Divider, Grid, Icon, Header, Menu, Popup } from 'src'
import Editor from 'docs/app/Components/Editor/Editor'
import { scrollToAnchor } from 'docs/app/utils'

const babelConfig = {
  presets: ['es2015', 'react', 'stage-1'],
}

const titleStyle = {
  margin: 0,
}
const descriptionStyle = {
  maxWidth: '50rem',
}

const headerColumnStyle = {
  // provide room for absolutely positions toggle code icons
  minHeight: '4em',
  paddingRight: '7em',
}

const childrenStyle = {
  paddingTop: 0,
  maxWidth: '50rem',
}

const errorStyle = {
  padding: '1em',
  fontSize: '0.9rem',
  color: '#a33',
  background: '#fff2f2',
}

const toolTipStyle = { width: 100, textAlign: 'center', padding: '0.5em' }
const ToolTip = ({ children, content }) => (
  <Popup
    inverted
    mouseEnterDelay={800}
    position='top center'
    size='tiny'
    style={toolTipStyle}
    trigger={children}
    content={content}
  />
)
ToolTip.propTypes = {
  children: PropTypes.node,
  content: PropTypes.node,
}

/**
 * Renders a `component` and the raw `code` that produced it.
 * Allows toggling the the raw `code` code block.
 */
class ComponentExample extends Component {
  static propTypes = {
    children: PropTypes.node,
    description: PropTypes.node,
    examplePath: PropTypes.string.isRequired,
    history: PropTypes.object.isRequired,
    location: PropTypes.object.isRequired,
    match: PropTypes.object.isRequired,
    title: PropTypes.node,
  }

  componentWillMount() {
    const { examplePath } = this.props
    const sourceCode = this.getOriginalSourceCode()

    this.anchorName = _.kebabCase(_.last(examplePath.split('/')))

    // show code for direct links to examples
    const showCode = this.anchorName === location.hash.replace('#', '')
    const exampleElement = this.renderOriginalExample()
    const markup = renderToStaticMarkup(exampleElement)

    this.setState({
      exampleElement,
      showCode,
      sourceCode,
      markup,
    })
  }

  setHashAndScroll = () => {
    const { history } = this.props
    history.replace(location.pathname + '#' + this.anchorName)
    scrollToAnchor()
  }

  removeHash = () => {
    const { history } = this.props
    history.replace(location.pathname)
  }

  handleDirectLinkClick = (e) => {
    e.preventDefault()
    this.setHashAndScroll()

    copyToClipboard(location.href)
    this.setState({ copiedDirectLink: true })

    setTimeout(() => this.setState({ copiedDirectLink: false }), 1000)
  }

  handleShowCodeClick = (e) => {
    e.preventDefault()

    const { showCode } = this.state
    this.setState({ showCode: !showCode })

    if (!showCode) this.setHashAndScroll()
    else this.removeHash()
  }

  handleShowHTMLClick = (e) => {
    e.preventDefault()

    const { showHTML } = this.state
    this.setState({ showHTML: !showHTML })

    if (!showHTML) this.setHashAndScroll()
    else this.removeHash()
  }

  copyJSX = () => {
    copyToClipboard(this.state.sourceCode)
    this.setState({ copiedCode: true })
    setTimeout(() => this.setState({ copiedCode: false }), 1000)
  }

  resetJSX = () => {
    const { sourceCode } = this.state
    const original = this.getOriginalSourceCode()
    if (sourceCode !== original && confirm('Lose your changes?')) { // eslint-disable-line no-alert
      this.setState({ sourceCode: original })
      this.renderSourceCode()
    }
  }

  getOriginalSourceCode = () => {
    const { examplePath } = this.props

    if (!this.sourceCode) this.sourceCode = require(`!raw-loader!../../Examples/${examplePath}`)

    return this.sourceCode
  }

  getKebabExamplePath = () => {
    if (!this.kebabExamplePath) this.kebabExamplePath = _.kebabCase(this.props.examplePath)

    return this.kebabExamplePath
  }

  renderError = _.debounce((error) => {
    this.setState({ error })
  }, 800)

  renderOriginalExample = () => {
    const { examplePath } = this.props
    return createElement(exampleContext(`./${examplePath}.js`).default)
  }

  renderSourceCode = _.debounce(() => {
    const { examplePath } = this.props
    const { sourceCode } = this.state
    // Heads Up!
    //
    // These are used in the code editor scope when rewriting imports to const statements
    // We use require() to preserve variable names
    // Webpack rewrites import names
    /* eslint-disable no-unused-vars */
    const FAKER = require('faker')
    const LODASH = require('lodash')
    const REACT = require('react')
    const SEMANTIC_UI_REACT = require('semantic-ui-react')
    let COMMON
    /* eslint-enable no-unused-vars */

    // Should use an AST transform here... oh well :/
    // Rewrite the example file into an IIFE that returns a component
    // which can be rendered in this ComponentExample's render() method

    // rewrite imports to const statements against the UPPERCASE module names
    const imports = _.get(/(^import[\s\S]*from[\s\S]*['"]\n)/.exec(sourceCode), '[1]', '')
      .replace(/[\s\n]+/g, ' ')         // normalize spaces and make one line
      .replace(/ import/g, '\nimport')  // one import per line
      .split('\n')                      // split lines
      .filter(Boolean)                  // remove empty lines
      .map(l => {                       // rewrite imports to const statements
        const [
          defaultImport,
          destructuredImports,
          _module,
        ] = _.tail(/import\s+([\w]+)?(?:\s*,\s*)?({[\s\w,]+})?\s+from\s+['"](?:.*\/)?([\w\-_]+)['"]/.exec(l))

        const module = _.snakeCase(_module).toUpperCase()

        if (module === 'COMMON') {
          const componentPath = examplePath.split(__PATH_SEP__).splice(0, 2).join(__PATH_SEP__)
          COMMON = require(`docs/app/Examples/${componentPath}/common`)
        }

        const constStatements = []
        if (defaultImport) constStatements.push(`const ${defaultImport} = ${module}`)
        if (destructuredImports) constStatements.push(`const ${destructuredImports} = ${module}`)
        constStatements.push('\n')

        return constStatements.join('\n')
      })
      .join('\n')

    // capture the default export so we can return it from the IIFE
    const defaultExport = _.get(/export\s+default\s+(?:class|function)?(?:\s+)?(\w+)/.exec(sourceCode), '[1]')

    // consider everything after the imports to be the body
    // remove `export` statements except `export default class|function`
    const body = _.get(/import[\s\S]*from.*\n([\s\S]*)/.exec(sourceCode), '[1]', '')
      .replace(/export\s+default\s+(?!class|function)\w+([\s\n]+)?/, '')  // remove `export default Foo` statements
      .replace(/export\s+default\s+/, '')                                 // remove `export default ...`

    const IIFE = `(function() {\n${imports}${body}return ${defaultExport}\n}())`

    try {
      const { code } = Babel.transform(IIFE, babelConfig)
      const Example = eval(code) // eslint-disable-line no-eval
      const exampleElement = _.isFunction(Example) ? <Example /> : Example

      if (!isValidElement(exampleElement)) {
        this.renderError('Default export is not a valid element. Type:' + {}.toString.call(exampleElement))
      } else {
        // immediately render a null error
        // but also ensure the last debounced error call is a null error
        const error = null
        this.renderError(error)
        this.setState({
          error,
          exampleElement,
          markup: renderToStaticMarkup(exampleElement),
        })
      }
    } catch (err) {
      this.renderError(err.message)
    }
  }, 100)

  handleChangeCode = (sourceCode) => {
    this.setState({ sourceCode })
    this.renderSourceCode()
  }

  setGitHubHrefs = () => {
    const { examplePath } = this.props

    if (this.ghEditHref && this.ghBugHref) return

    // get component name from file path:
    // elements/Button/Types/ButtonButtonExample
    const pathParts = examplePath.split(__PATH_SEP__)
    const componentName = pathParts[1]
    const filename = pathParts[pathParts.length - 1]

    this.ghEditHref = [
      `${repoURL}/edit/master/docs/app/Examples/${examplePath}.js`,
      `?message=docs(${filename}): your description`,
    ].join('')

    this.ghBugHref = [
      `${repoURL}/issues/new?`,
      _.map({
        title: `fix(${componentName}): your description`,
        body: [
          '**Steps to Reproduce**',
          '1. Do something',
          '2. Do something else.',
          '',
          '**Expected**',
          `The ${componentName} should do this`,
          '',
          '**Result**',
          `The ${componentName} does not do this`,
          '',
          '**Testcase**',
          `If the docs show the issue, use: ${window.location.href}`,
          'Otherwise, fork this to get started: http://codepen.io/levithomason/pen/ZpBaJX',
        ].join('\n'),
      }, (val, key) => `${key}=${encodeURIComponent(val)}`).join('&'),
    ].join('')
  }

  renderJSXControls = () => {
    const { copiedCode, error } = this.state

    this.setGitHubHrefs()

    const color = error ? 'red' : 'black'
    return (
      <Divider horizontal fitted>
        <Menu text>
          <Menu.Item
            active={copiedCode || !!error} // to show the color
            color={copiedCode ? 'green' : color}
            onClick={this.copyJSX}
            icon={!copiedCode && 'copy'}
            content={copiedCode ? 'Copied!' : 'Copy'}
          />
          <Menu.Item
            active={!!error} // to show the color
            color={color}
            icon='refresh'
            content='Reset'
            onClick={this.resetJSX}
          />
          <Menu.Item
            active={!!error} // to show the color
            color={color}
            icon='github'
            content='Edit'
            href={this.ghEditHref}
            target='_blank'
          />
          <Menu.Item
            active={!!error} // to show the color
            color={color}
            icon='bug'
            content='Issue'
            href={this.ghBugHref}
            target='_blank'
          />
        </Menu>
      </Divider>
    )
  }

  renderJSX = () => {
    const { error, showCode, sourceCode } = this.state
    if (!showCode) return

    const style = { width: '100%' }
    if (error) {
      style.boxShadow = `inset 0 0 0 1em ${errorStyle.background}`
    }

    return (

        {this.renderJSXControls()}
        <Editor
          id={`${this.getKebabExamplePath()}-jsx`}
          value={sourceCode}
          onChange={this.handleChangeCode}
        />
        {error && (

{error}

        )}

    )
  }

  renderHTML = () => {
    const { showHTML, markup } = this.state
    if (!showHTML) return

    // add new lines between almost all adjacent elements
    // moves inline elements to their own line
    const preFormattedHTML = markup
      .replace(/><(?!\/i|\/label|\/span|option)/g, '>\n<')

    const beautifiedHTML = html(preFormattedHTML, {
      indent_size: 2,
      indent_char: ' ',
      wrap_attributes: 'auto',
      wrap_attributes_indent_size: 2,
      end_with_newline: false,
    })

    return (

        <Divider horizontal>Rendered HTML</Divider>
        <Editor id={`${this.getKebabExamplePath()}-html`} mode='html' value={beautifiedHTML} readOnly />

    )
  }

  render() {
    const { children, description, title } = this.props
    const { copiedDirectLink, exampleElement, showCode, showHTML } = this.state
    const exampleStyle = {}

    if (showCode || showHTML || location.hash === `#${this.anchorName}`) {
      exampleStyle.boxShadow = '0 0 30px #ccc'
    }

    return (
      <Grid className='docs-example' style={exampleStyle} divided={showCode} columns='1' id={this.anchorName}>
        <Grid.Column style={headerColumnStyle}>
          {title && }
          {description &&
{description}
}
          <Menu compact text icon size='small' color='green' className='docs-example-menu'>
            <ToolTip content={copiedDirectLink ? ' Copied Link!' : 'Direct link'}>
              <Menu.Item href={`#${this.anchorName}`} onClick={this.handleDirectLinkClick}>
                <Icon size='large' color='grey' name='linkify' fitted />
              </Menu.Item>
            </ToolTip>
            <ToolTip content='Full Screen'>
              <Menu.Item href={`/maximize/${this.anchorName}`} target='_blank'>
                <Icon size='large' color='grey' name='window maximize' fitted />
              </Menu.Item>
            </ToolTip>
            <ToolTip content='Show HTML'>
              <Menu.Item active={showHTML} onClick={this.handleShowHTMLClick}>
                <Icon size='large' color={showHTML ? 'green' : 'grey'} name='html5' fitted />
              </Menu.Item>
            </ToolTip>
            <ToolTip content='Edit Code'>
              <Menu.Item active={showCode} onClick={this.handleShowCodeClick}>
                <Icon size='large' name='code' fitted />
              </Menu.Item>
            </ToolTip>
          </Menu>
        </Grid.Column>
        {children && (
          <Grid.Column style={childrenStyle}>
            {children}
          </Grid.Column>
        )}
        <Grid.Column className={`rendered-example ${this.getKebabExamplePath()}`}>
          {exampleElement}
        </Grid.Column>
        {this.renderJSX()}
        {this.renderHTML()}
      </Grid>
    )
  }
}

export default withRouter(ComponentExample)
<|endoftext|>


<commit_before>const h = require('virtual-dom/h');

module.exports = function (data) {
  const {gameID, game} = data;
  return h('div', {className: 'room'}, [
    h('a', {href: '/new-player/' + gameID}, 'Wanna play along?'),
    h('div', {id: 'players'}, [
      h('ul', Object.keys(game.players).map(playerID => {
        const player = players[playerID];
        return h('li', {
          style: `background-color: rgb(${player.color[1]}, ${player.color[0]}, ${player.color[2]});`,
          dataPlayerId: playerID
        })
      }))
    ])
  ]);
};

<commit_msg>Fix background-color bug<commit_after>const h = require('virtual-dom/h');

module.exports = function (data) {
  const {gameID, game} = data;
  return h('div', {className: 'room'}, [
    h('a', {href: '/new-player/' + gameID}, 'Wanna play along?'),
    h('div', {id: 'players'}, [
      h('ul', Object.keys(game.players).map(playerID => {
        const player = game.players[playerID];
        return h('li', {
          style: {
            backgroundColor: `rgb(${player.color[1]}, ${player.color[0]}, ${player.color[2]})`
          },
          dataPlayerId: playerID
        })
      }))
    ])
  ]);
};

<|endoftext|>


<commit_before>/*
 * Axelor Business Solutions
 *
 * Copyright (C) 2005-2016 Axelor (<http://axelor.com>).
 *
 * This program is free software: you can redistribute it and/or  modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
(function() {

"use strict";

var ui = angular.module('axelor.ui');

function BaseCardsCtrl(type, $scope, $element) {

	ui.DSViewCtrl(type, $scope, $element);

	$scope.getRouteOptions = function() {
		return {
			mode: type,
			args: []
		};
	};

	$scope.setRouteOptions = function(options) {
		if (!$scope.isNested) {
			$scope.updateRoute();
		}
	};

	var ds = $scope._dataSource;
	var initialized = false;

	$scope.onShow = function (viewPromise) {

		if (initialized) {
			return $scope.onRefresh();
		}

		initialized = true;

		viewPromise.then(function (meta) {
			$scope.parse(meta.fields, meta.view);
		});
	};

	$scope.parse = function (fields, view) {

	};

	$scope.onNew = function () {
		$scope.switchTo('form', function (formScope) {
			formScope.edit(null);
			formScope.setEditable();
			formScope.$broadcast("on:new");
		});
	};

	$scope.onRefresh = function () {
		return $scope._dataSource.search().success(update);
	};

	function update(records) {
		$scope.records = records;
	}

	$scope.filter = function(options) {
		var view = $scope.schema;
		var opts = {
			fields: _.pluck($scope.fields, 'name')
		};

		if (options.criteria || options._domains) {
			opts.filter = options;
		}
		if (options.archived !== undefined) {
			opts.archived = options.archived;
		}
		if (view.orderBy) {
			opts.sortBy = view.orderBy.split(',');
		}

		return ds.search(opts).success(update);
	};

	$scope.pagerText = function() {
		var page = ds._page;
		if (page && page.from !== undefined) {
			if (page.total === 0) return null;
			return _t("{0} to {1} of {2}", page.from + 1, page.to, page.total);
		}
	};

	$scope.onNext = function() {
		var fields = _.pluck($scope.fields, 'name');
		return ds.next(fields).success(update);
	};

	$scope.onPrev = function() {
		var fields = _.pluck($scope.fields, 'name');
		return ds.prev(fields).success(update);
	};
}

ui.controller("CardsCtrl", ['$scope', '$element', function CardsCtrl($scope, $element) {

	BaseCardsCtrl.call(this, 'cards', $scope, $element);

	$scope.viewItems = {};

	$scope.parse = function (fields, view) {
		var viewItems = {};
		_.each(view.items, function (item) {
			if (item.name) {
				viewItems[item.name] = _.extend({}, item, fields[item.name], item.widgetAttrs);
			}
		});
		$scope.viewItems = viewItems;
		$scope.onRefresh();
		$scope.waitForActions(axelor.$adjustSize);
	};

	$scope.onExport = function (full) {
		var fields = full ? [] : _.pluck($scope.viewItems, 'name');
		return $scope._dataSource.export_(fields).success(function(res) {
			var fileName = res.fileName;
			var filePath = 'ws/rest/' + $scope._model + '/export/' + fileName;
			ui.download(filePath, fileName);
		});
	};
}]);

ui.controller("KanbanCtrl", ['$scope', '$element', function KanbanCtrl($scope, $element) {

	BaseCardsCtrl.call(this, 'kanban', $scope, $element);

	$scope.parse = function (fields, view) {
		var columnBy = fields[view.columnBy] || {};
		var columns = _.map(columnBy.selectionList, function (item) {
			return item;
		});

		columns = _.first(columns, 6);

		var first = _.first(columns);
		if (view.onNew) {
			first.canCreate = true;
		}

		var sequenceBy = fields[view.sequenceBy] || {};
		if (["integer", "long"].indexOf(sequenceBy.type) === -1) {
			throw new Error("Invalid sequenceBy field in view: " + view.name);
		}

		$scope.columns = columns;
		$scope.colSpan = "kanban-cs-" + columns.length;
	};

	$scope.move = function (record, to, next, prev) {

		var view = $scope.schema;
		var rec = _.pick(record, "id", "version", view.sequenceBy);
		var ds = $scope._dataSource._new($scope._model);

		// update columnBy
		rec[view.columnBy] = to;

		// update sequenceBy
		var all = _.compact([prev, rec, next]);
		var offset = _.min(_.pluck(all, view.sequenceBy)) || 0;

		_.each(all, function (item, i) {
			item[view.sequenceBy] = offset + i;
		});

		return ds.saveAll(all).success(function (records) {
			_.each(all, function (item) {
				_.extend(item, ds.get(item.id));
			});
			record.version = rec.version;
		});
	};

	$scope.onRefresh = function () {
		$scope.$broadcast("on:refresh");
	};

	$scope.filter = function(searchFilter) {
		var options = {};
		if (searchFilter.criteria || searchFilter._domains) {
			options = {
				filter: searchFilter
			};
			if (searchFilter.archived !== undefined) {
				options.archived = searchFilter.archived;
			}
			$scope.$broadcast("on:filter", options);
		}
	};

	$scope.sortableOptions = {
		connectWith: ".kanban-card-list",
		items: ".kanban-card",
		tolerance: "pointer",
		stop: function (event, ui) {
			var item = ui.item;
			var sortable = item.sortable;
			var source = sortable.source.scope();
			var target = (sortable.droptarget || $(this)).scope();

			var next = item.next().scope();
			var prev = item.prev().scope();
			if (next) next = next.record;
			if (prev) prev = prev.record;

			var index = sortable.dropindex;
			if (source === target && sortable.index === index) {
				return;
			}

			$scope.move(target.records[index], target.column.value, next, prev);
			$scope.$applyAsync();
		}
	};
}]);

ui.directive('uiKanbanColumn', ["ActionService", function (ActionService) {

	return {
		scope: true,
		link: function (scope, element, attrs) {

			var ds = scope._dataSource._new(scope._model);
			var view = scope.schema;
			var elemMore = element.children(".kanban-more");

			ds._context = _.extend({}, scope._dataSource._context);
			ds._context[view.columnBy] = scope.column.value;
			ds._page.limit = view.limit || 20;

			var domain = "self." + view.columnBy + " = :" + view.columnBy;
			ds._domain = ds._domain ? ds._domain + " AND " + domain : domain;

			scope.records = [];

			function fetch(options) {
				var opts = _.extend({
					offset: 0,
					sortBy: [view.sequenceBy]
				}, options);
				elemMore.hide();
				return ds.search(opts).success(function (records) {
					scope.records = scope.records.concat(records);
					elemMore.fadeIn('slow');
				});
			}

			scope.hasMore = function () {
				var page = ds._page;
				var next = page.from + page.limit;
				return next < page.total;
			};

			scope.onMore = function () {
				var page = ds._page;
				var next = scope.records.length;
				if (next < page.total) {
					return fetch({
						offset: next
					});
				}
			};

			var onNew = null;

			scope.getContext = function () {
				var ctx = _.extend({}, scope._context);
				ctx._value = scope.newItem;
				return ctx;
			};

			scope.newItem = null;
			scope.onCreate = function () {

				var rec = scope.record = {};
				var view = scope.schema;

				rec[view.columnBy] = scope.column.value;

				if (onNew === null) {
					onNew = ActionService.handler(scope, element, {
						action: view.onNew
					});
				}

				var ds = scope._dataSource;
				var promise = onNew.handle();
				promise.then(function () {
					ds.save(scope.record).success(function (rec) {
						scope.newItem = null;
						scope.records.unshift(rec);
					});
				});
			};

			scope.onEdit = function (record) {
				scope.switchTo('form', function (formScope) {
					if (formScope.canEdit()) {
						formScope.edit(record);
						formScope.setEditable();
					}
				});
			};

			scope.onDelete = function (record) {
				axelor.dialogs.confirm(_t("Do you really want to delete the selected record?"),
				function(confirmed) {
					if (!confirmed) {
						return;
					}
					ds.removeAll([record]).success(function(records, page) {
						var index = scope.records.indexOf(record);
						scope.records.splice(index, 1);
					});
				});
			};

			scope.$on("on:refresh", function (e) {
				scope.newItem = null;
				scope.records.length = 0;
				fetch();
			});

			scope.$on("on:filter", function (e, options) {
				scope.newItem = null;
				scope.records.length = 0;
				return fetch(options);
			});

			fetch();
		}
	};
}]);

ui.directive('uiCards', function () {

	return function (scope, element, attrs) {

		var onRefresh = scope.onRefresh;
		scope.onRefresh = function () {
			scope.records = null;
			return onRefresh.apply(scope, arguments);
		};

		scope.onEdit = function (record, readonly) {
			var ds = scope._dataSource;
			var page = ds._page;
			page.index = record ? ds._data.indexOf(record) : -1;
			scope.switchTo('form', function (formScope) {
				if (!readonly && formScope.canEdit()) {
					formScope.setEditable();
				}
			});
		};

		scope.onDelete = function (record) {
			axelor.dialogs.confirm(_t("Do you really want to delete the selected record?"),
			function(confirmed) {
				if (!confirmed) {
					return;
				}
				var ds = scope._dataSource;
				ds.removeAll([record]).success(function(records, page) {
					var index = scope.records.indexOf(record);
					scope.records.splice(index, 1);
				});
			});
		};

		element.on("click", ".kanban-card", function (e) {
			if ($(e.target).parents(".kanban-card-menu").size()) {
				return;
			}
			var elem = $(this);
			var record = elem.scope().record;
			scope.onEdit(record, true);
			scope.applyLater();
		});
	};
});

ui.directive('uiCard', ["$parse", "$compile", function ($parse, $compile) {

	return {
		scope: true,
		link: function (scope, element, attrs) {

			var body = element.find(".kanban-card-body");
			var record = scope.record;
			var evalScope = scope.$new(true);

			evalScope.record = record;
			evalScope.getContext = scope.getContext = function () {
				var ctx = _.extend({}, scope._context, scope.record);
				ctx._model = scope._model;
				return ctx;
			};

			if (!record.$processed) {
				element.hide();
			}

			function process(record) {
				if (record.$processed) {
					return record;
				}
				record.$processed = true;
				for (var name in record) {
					if (!record.hasOwnProperty(name) || name.indexOf('.') === -1) {
						continue;
					}
					var nested = record;
					var names = name.split('.');
					var head = _.first(names, names.length - 1);
					var last = _.last(names);
					var i, n;
					for (i = 0; i < head.length; i++) {
						n = head[i];
						nested = nested[n] || (nested[n] = {});
					}
					nested[last] = record[name];
				}
				return record;
			}

			evalScope.$watch("record", function (record) {
				_.extend(evalScope, process(record));
			}, true);

			evalScope.$image = function (fieldName, imageName) {
				var rec = scope.record;
				var v = rec.version || rec.$version || 0;
				if (fieldName === null && imageName) {
					return "ws/rest/" + scope._model + "/" + rec.id + "/" + imageName + "/download?image=true&v=" + v;
				}
				var field = scope.fields[fieldName];
				if (field && field.target && rec[fieldName]) {
					var val = rec[fieldName];
					return "ws/rest/" + field.target + "/" + val.id + "/" + imageName + "/download?image=true&v=" + v;
				}
				return "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
			};

			evalScope.$fmt = function (fieldName) {
				var value = evalScope[fieldName];
				if (value === undefined || value === null) {
					return "";
				}
				var field = scope.viewItems[fieldName] || scope.fields[fieldName];
				if (!field) {
					return value;
				}
				var type = field.selection ? "selection" : field.type;
				var formatter = ui.formatters[type];
				if (formatter) {
					return formatter(field, value);
				}
				return value;
			};

			var template = (scope.schema.template || "").trim();
			if (template.indexOf('<') !== 0) {
				template = "" + template + "";
			}

			scope.hilite = null;

			$compile(template)(evalScope).appendTo(body);

			var hilites = scope.schema.hilites || [];
			for (var i = 0; i < hilites.length; i++) {
				var hilite = hilites[i];
				var expr = $parse(hilite.condition);
				if (expr(scope.record)) {
					scope.hilite = hilite;
					break;
				}
			}

			if (scope.schema.cardWidth) {
				element.parent().css("width", scope.schema.cardWidth);
			}

			element.fadeIn("slow");
		}
	};
}]);

})();
<commit_msg>Fix cards/kanban view data loading issue<commit_after>/*
 * Axelor Business Solutions
 *
 * Copyright (C) 2005-2016 Axelor (<http://axelor.com>).
 *
 * This program is free software: you can redistribute it and/or  modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
(function() {

"use strict";

var ui = angular.module('axelor.ui');

function BaseCardsCtrl(type, $scope, $element) {

	ui.DSViewCtrl(type, $scope, $element);

	$scope.getRouteOptions = function() {
		return {
			mode: type,
			args: []
		};
	};

	$scope.setRouteOptions = function(options) {
		if (!$scope.isNested) {
			$scope.updateRoute();
		}
	};

	var ds = $scope._dataSource;
	var initialized = false;

	$scope.onShow = function (viewPromise) {

		if (initialized) {
			return $scope.onRefresh();
		}

		initialized = true;

		viewPromise.then(function (meta) {
			$scope.parse(meta.fields, meta.view);
		});
	};

	$scope.parse = function (fields, view) {

	};

	$scope.onNew = function () {
		$scope.switchTo('form', function (formScope) {
			formScope.edit(null);
			formScope.setEditable();
			formScope.$broadcast("on:new");
		});
	};

	$scope.onRefresh = function () {
		return $scope.filter({});
	};

	function update(records) {
		$scope.records = records;
	}

	$scope.filter = function(options) {
		var view = $scope.schema;
		var opts = {
			fields: _.pluck($scope.fields, 'name')
		};

		if (options.criteria || options._domains) {
			opts.filter = options;
		}
		if (options.archived !== undefined) {
			opts.archived = options.archived;
		}
		if (view.orderBy) {
			opts.sortBy = view.orderBy.split(',');
		}

		return ds.search(opts).success(update);
	};

	$scope.pagerText = function() {
		var page = ds._page;
		if (page && page.from !== undefined) {
			if (page.total === 0) return null;
			return _t("{0} to {1} of {2}", page.from + 1, page.to, page.total);
		}
	};

	$scope.onNext = function() {
		var fields = _.pluck($scope.fields, 'name');
		return ds.next(fields).success(update);
	};

	$scope.onPrev = function() {
		var fields = _.pluck($scope.fields, 'name');
		return ds.prev(fields).success(update);
	};
}

ui.controller("CardsCtrl", ['$scope', '$element', function CardsCtrl($scope, $element) {

	BaseCardsCtrl.call(this, 'cards', $scope, $element);

	$scope.viewItems = {};

	$scope.parse = function (fields, view) {
		var viewItems = {};
		_.each(view.items, function (item) {
			if (item.name) {
				viewItems[item.name] = _.extend({}, item, fields[item.name], item.widgetAttrs);
			}
		});
		$scope.viewItems = viewItems;
		$scope.onRefresh();
		$scope.waitForActions(axelor.$adjustSize);
	};

	$scope.onExport = function (full) {
		var fields = full ? [] : _.pluck($scope.viewItems, 'name');
		return $scope._dataSource.export_(fields).success(function(res) {
			var fileName = res.fileName;
			var filePath = 'ws/rest/' + $scope._model + '/export/' + fileName;
			ui.download(filePath, fileName);
		});
	};
}]);

ui.controller("KanbanCtrl", ['$scope', '$element', function KanbanCtrl($scope, $element) {

	BaseCardsCtrl.call(this, 'kanban', $scope, $element);

	$scope.parse = function (fields, view) {
		var columnBy = fields[view.columnBy] || {};
		var columns = _.map(columnBy.selectionList, function (item) {
			return item;
		});

		columns = _.first(columns, 6);

		var first = _.first(columns);
		if (view.onNew) {
			first.canCreate = true;
		}

		var sequenceBy = fields[view.sequenceBy] || {};
		if (["integer", "long"].indexOf(sequenceBy.type) === -1) {
			throw new Error("Invalid sequenceBy field in view: " + view.name);
		}

		$scope.columns = columns;
		$scope.colSpan = "kanban-cs-" + columns.length;
	};

	$scope.move = function (record, to, next, prev) {

		var view = $scope.schema;
		var rec = _.pick(record, "id", "version", view.sequenceBy);
		var ds = $scope._dataSource._new($scope._model);

		// update columnBy
		rec[view.columnBy] = to;

		// update sequenceBy
		var all = _.compact([prev, rec, next]);
		var offset = _.min(_.pluck(all, view.sequenceBy)) || 0;

		_.each(all, function (item, i) {
			item[view.sequenceBy] = offset + i;
		});

		return ds.saveAll(all).success(function (records) {
			_.each(all, function (item) {
				_.extend(item, ds.get(item.id));
			});
			record.version = rec.version;
		});
	};

	$scope.onRefresh = function () {
		$scope.$broadcast("on:refresh");
	};

	$scope.filter = function(searchFilter) {
		var options = {};
		if (searchFilter.criteria || searchFilter._domains) {
			options = {
				filter: searchFilter
			};
			if (searchFilter.archived !== undefined) {
				options.archived = searchFilter.archived;
			}
			$scope.$broadcast("on:filter", options);
		}
	};

	$scope.sortableOptions = {
		connectWith: ".kanban-card-list",
		items: ".kanban-card",
		tolerance: "pointer",
		stop: function (event, ui) {
			var item = ui.item;
			var sortable = item.sortable;
			var source = sortable.source.scope();
			var target = (sortable.droptarget || $(this)).scope();

			var next = item.next().scope();
			var prev = item.prev().scope();
			if (next) next = next.record;
			if (prev) prev = prev.record;

			var index = sortable.dropindex;
			if (source === target && sortable.index === index) {
				return;
			}

			$scope.move(target.records[index], target.column.value, next, prev);
			$scope.$applyAsync();
		}
	};
}]);

ui.directive('uiKanbanColumn', ["ActionService", function (ActionService) {

	return {
		scope: true,
		link: function (scope, element, attrs) {

			var ds = scope._dataSource._new(scope._model);
			var view = scope.schema;
			var elemMore = element.children(".kanban-more");

			ds._context = _.extend({}, scope._dataSource._context);
			ds._context[view.columnBy] = scope.column.value;
			ds._page.limit = view.limit || 20;

			var domain = "self." + view.columnBy + " = :" + view.columnBy;
			ds._domain = ds._domain ? ds._domain + " AND " + domain : domain;

			scope.records = [];

			function fetch(options) {
				var opts = _.extend({
					offset: 0,
					sortBy: [view.sequenceBy]
				}, options);
				elemMore.hide();
				return ds.search(opts).success(function (records) {
					scope.records = scope.records.concat(records);
					elemMore.fadeIn('slow');
				});
			}

			scope.hasMore = function () {
				var page = ds._page;
				var next = page.from + page.limit;
				return next < page.total;
			};

			scope.onMore = function () {
				var page = ds._page;
				var next = scope.records.length;
				if (next < page.total) {
					return fetch({
						offset: next
					});
				}
			};

			var onNew = null;

			scope.getContext = function () {
				var ctx = _.extend({}, scope._context);
				ctx._value = scope.newItem;
				return ctx;
			};

			scope.newItem = null;
			scope.onCreate = function () {

				var rec = scope.record = {};
				var view = scope.schema;

				rec[view.columnBy] = scope.column.value;

				if (onNew === null) {
					onNew = ActionService.handler(scope, element, {
						action: view.onNew
					});
				}

				var ds = scope._dataSource;
				var promise = onNew.handle();
				promise.then(function () {
					ds.save(scope.record).success(function (rec) {
						scope.newItem = null;
						scope.records.unshift(rec);
					});
				});
			};

			scope.onEdit = function (record) {
				scope.switchTo('form', function (formScope) {
					if (formScope.canEdit()) {
						formScope.edit(record);
						formScope.setEditable();
					}
				});
			};

			scope.onDelete = function (record) {
				axelor.dialogs.confirm(_t("Do you really want to delete the selected record?"),
				function(confirmed) {
					if (!confirmed) {
						return;
					}
					ds.removeAll([record]).success(function(records, page) {
						var index = scope.records.indexOf(record);
						scope.records.splice(index, 1);
					});
				});
			};

			scope.$on("on:refresh", function (e) {
				scope.newItem = null;
				scope.records.length = 0;
				fetch();
			});

			scope.$on("on:filter", function (e, options) {
				scope.newItem = null;
				scope.records.length = 0;
				return fetch(options);
			});

			fetch();
		}
	};
}]);

ui.directive('uiCards', function () {

	return function (scope, element, attrs) {

		var onRefresh = scope.onRefresh;
		scope.onRefresh = function () {
			scope.records = null;
			return onRefresh.apply(scope, arguments);
		};

		scope.onEdit = function (record, readonly) {
			var ds = scope._dataSource;
			var page = ds._page;
			page.index = record ? ds._data.indexOf(record) : -1;
			scope.switchTo('form', function (formScope) {
				if (!readonly && formScope.canEdit()) {
					formScope.setEditable();
				}
			});
		};

		scope.onDelete = function (record) {
			axelor.dialogs.confirm(_t("Do you really want to delete the selected record?"),
			function(confirmed) {
				if (!confirmed) {
					return;
				}
				var ds = scope._dataSource;
				ds.removeAll([record]).success(function(records, page) {
					var index = scope.records.indexOf(record);
					scope.records.splice(index, 1);
				});
			});
		};

		element.on("click", ".kanban-card", function (e) {
			if ($(e.target).parents(".kanban-card-menu").size()) {
				return;
			}
			var elem = $(this);
			var record = elem.scope().record;
			scope.onEdit(record, true);
			scope.applyLater();
		});
	};
});

ui.directive('uiCard', ["$parse", "$compile", function ($parse, $compile) {

	return {
		scope: true,
		link: function (scope, element, attrs) {

			var body = element.find(".kanban-card-body");
			var record = scope.record;
			var evalScope = scope.$new(true);

			evalScope.record = record;
			evalScope.getContext = scope.getContext = function () {
				var ctx = _.extend({}, scope._context, scope.record);
				ctx._model = scope._model;
				return ctx;
			};

			if (!record.$processed) {
				element.hide();
			}

			function process(record) {
				if (record.$processed) {
					return record;
				}
				record.$processed = true;
				for (var name in record) {
					if (!record.hasOwnProperty(name) || name.indexOf('.') === -1) {
						continue;
					}
					var nested = record;
					var names = name.split('.');
					var head = _.first(names, names.length - 1);
					var last = _.last(names);
					var i, n;
					for (i = 0; i < head.length; i++) {
						n = head[i];
						nested = nested[n] || (nested[n] = {});
					}
					nested[last] = record[name];
				}
				return record;
			}

			evalScope.$watch("record", function (record) {
				_.extend(evalScope, process(record));
			}, true);

			evalScope.$image = function (fieldName, imageName) {
				var rec = scope.record;
				var v = rec.version || rec.$version || 0;
				if (fieldName === null && imageName) {
					return "ws/rest/" + scope._model + "/" + rec.id + "/" + imageName + "/download?image=true&v=" + v;
				}
				var field = scope.fields[fieldName];
				if (field && field.target && rec[fieldName]) {
					var val = rec[fieldName];
					return "ws/rest/" + field.target + "/" + val.id + "/" + imageName + "/download?image=true&v=" + v;
				}
				return "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
			};

			evalScope.$fmt = function (fieldName) {
				var value = evalScope[fieldName];
				if (value === undefined || value === null) {
					return "";
				}
				var field = scope.viewItems[fieldName] || scope.fields[fieldName];
				if (!field) {
					return value;
				}
				var type = field.selection ? "selection" : field.type;
				var formatter = ui.formatters[type];
				if (formatter) {
					return formatter(field, value);
				}
				return value;
			};

			var template = (scope.schema.template || "").trim();
			if (template.indexOf('<') !== 0) {
				template = "" + template + "";
			}

			scope.hilite = null;

			$compile(template)(evalScope).appendTo(body);

			var hilites = scope.schema.hilites || [];
			for (var i = 0; i < hilites.length; i++) {
				var hilite = hilites[i];
				var expr = $parse(hilite.condition);
				if (expr(scope.record)) {
					scope.hilite = hilite;
					break;
				}
			}

			if (scope.schema.cardWidth) {
				element.parent().css("width", scope.schema.cardWidth);
			}

			element.fadeIn("slow");
		}
	};
}]);

})();
<|endoftext|>


<commit_before>module.exports = function (schema) {
    const promise = require('bluebird');

    return {
        defineCreateExperimentSchema,
        defineUpdateExperimentSchema,
        defineCreateExperimentTaskSchema,
        defineUpdateExperimentTaskSchema,
        defineCreateExperimentNoteSchema,
        defineUpdateExperimentNoteSchema,
        defineUpdateExperimentTaskTemplatePropsSchema,
        defineTemplatePropertySchema,
        defineTemplateCommandSchema
    };

    function defineCreateExperimentSchema() {
        let createExperimentSchema = schema.defineSchema('CreateExperiment', {
            project_id: {
                type: 'string',
                nullable: false
            },

            name: {
                type: 'string',
                nullable: false
            },

            aim: {
                type: 'string',
                nullable: false
            },

            goal: {
                type: 'string',
                nullable: false
            },

            status: {
                type: 'string',
                nullable: false,
                isValidExperimentStatus: true
            },

            description: {
                type: 'string'
            }
        });

        createExperimentSchema.setDefaults({
            aim: '',
            goal: '',
            description: '',
            status: 'active'
        });
        createExperimentSchema.validateAsync = promise.promisify(createExperimentSchema.validate);
        return createExperimentSchema;
    }

    function defineUpdateExperimentSchema() {
        let updateExperimentSchema = schema.defineSchema('UpdateExperiment', {
            name: {
                type: 'string',
                nullable: true
            },

            description: {
                type: 'string',
                nullable: true
            },

            note: {
                type: 'string',
                nullable: true
            },

            goals: {
                type: 'array',
                nullable: true
            },

            collaborators: {
                type: 'array',
                nullable: true
            },

            funding: {
                type: 'array',
                nullable: true
            },

            citations: {
                type: 'array',
                nullable: true
            },

            papers: {
                type: 'array',
                nullable: true
            },

            publications: {
                type: 'array',
                nullable: true
            },

            status: {
                type: 'string',
                nullable: true
            }
        });
        updateExperimentSchema.setDefaults({});
        updateExperimentSchema.validateAsync = promise.promisify(updateExperimentSchema.validate);
        return updateExperimentSchema;
    }

    function defineCreateExperimentTaskSchema() {
        let createExperimentTaskSchema = schema.defineSchema('CreateExperimentTask', {
            name: {
                type: 'string',
                nullable: false
            },
            note: {
                type: 'string',
                nullable: false
            },
            parent_id: {
                type: 'string',
                nullable: false
            },
            index: {
                type: 'integer',
                nullable: false,
                min: 0
            }
        });

        createExperimentTaskSchema.setDefaults({parent_id: '', note: ''});
        createExperimentTaskSchema.validateAsync = promise.promisify(createExperimentTaskSchema.validate);
        return createExperimentTaskSchema;
    }

    function defineUpdateExperimentTaskSchema() {
        let updateExperimentTaskSchema = schema.defineSchema('UpdateExperimentTask', {
            name: {
                type: 'string',
                nullable: true
            },
            note: {
                type: 'string',
                nullable: true
            },
            parent_id: {
                type: 'string',
                nullable: true
            },
            flags: {
                nullable: true,
                flagged: {
                    type: 'boolean',
                    nullable: true
                },
                done: {
                    type: 'boolean',
                    nullable: true
                },
                starred: {
                    type: 'boolean',
                    nullable: true
                }
            },

            swap: {
                nullable: true,
                task_id: {
                    type: 'string',
                    nullable: true
                }
            }
        });

        updateExperimentTaskSchema.setDefaults({parent_id: ''});
        updateExperimentTaskSchema.validateAsync = promise.promisify(updateExperimentTaskSchema.validate);
        return updateExperimentTaskSchema;
    }

    function defineCreateExperimentNoteSchema() {
        let createExperimentNoteSchema = schema.defineSchema('CreateExperimentNote', {
            name: {
                type: 'string',
                nullable: false
            },
            note: {
                type: 'string',
                nullable: false
            }
        });

        createExperimentNoteSchema.setDefaults({});
        createExperimentNoteSchema.validateAsync = promise.promisify(createExperimentNoteSchema.validate);
        return createExperimentNoteSchema;
    }

    function defineUpdateExperimentNoteSchema() {
        let updateExperimentNoteSchema = schema.defineSchema('UpdateExperimentNote', {
            name: {
                type: 'string'
            },
            note: {
                type: 'string'
            }
        });

        updateExperimentNoteSchema.setDefaults({});
        updateExperimentNoteSchema.validateAsync = promise.promisify(updateExperimentNoteSchema.validate);
        return updateExperimentNoteSchema;
    }

    function defineUpdateExperimentTaskTemplatePropsSchema() {
        let updateExperimentTaskTemplatePropsSchema = schema.defineSchema('updateExperimentTaskTemplatePropsSchema', {
            properties: {
                type: 'array',
                nullable: false
            },

            template_id: {
                type: 'string',
                nullable: false
            }
        });
        updateExperimentTaskTemplatePropsSchema.setDefaults({});
        updateExperimentTaskTemplatePropsSchema.validateAsync = promise.promisify(updateExperimentTaskTemplatePropsSchema.validate);
        return updateExperimentTaskTemplatePropsSchema;
    }

    function defineTemplatePropertySchema() {
        let templatePropertySchema = schema.defineSchema('templatePropertySchema', {
            id: {
                type: 'string',
                nullable: false
            },

            otype: {
                type: 'string',
                nullable: false
            },

            setup_attribute: {
                type: 'string',
                nullable: false
            },

            setup_id: {
                type: 'string',
                nullable: false
            },

            attribute: {
                type: 'string',
                nullable: false
            },

            description: {
                type: 'string',
                nullable: false
            },

            name: {
                type: 'string',
                nullable: false
            },

            unit: {
                type: 'string',
                nullable: false
            },

            value: {
                nullable: false
            }
        });

        templatePropertySchema.setDefaults({});
        templatePropertySchema.validateAsync = promise.promisify(templatePropertySchema.validate);
        return templatePropertySchema;
    }

    function defineTemplateCommandSchema() {
        let templateCommandSchema = schema.defineSchema('templateCommandSchema', {
            id: {
                type: 'string',
                nullable: false
            },

            property_set_id: {
                type: 'string',
                nullable: true
            },

            command: {
                type: 'string',
                nullable: false
            }
        });

        templateCommandSchema.setDefaults({
            property_set_id: ''
        });
        templateCommandSchema.validateAsync = promise.promisify(templateCommandSchema.validate);
        return templateCommandSchema;
    }
};
<commit_msg>Allow properties without units<commit_after>module.exports = function (schema) {
    const promise = require('bluebird');

    return {
        defineCreateExperimentSchema,
        defineUpdateExperimentSchema,
        defineCreateExperimentTaskSchema,
        defineUpdateExperimentTaskSchema,
        defineCreateExperimentNoteSchema,
        defineUpdateExperimentNoteSchema,
        defineUpdateExperimentTaskTemplatePropsSchema,
        defineTemplatePropertySchema,
        defineTemplateCommandSchema
    };

    function defineCreateExperimentSchema() {
        let createExperimentSchema = schema.defineSchema('CreateExperiment', {
            project_id: {
                type: 'string',
                nullable: false
            },

            name: {
                type: 'string',
                nullable: false
            },

            aim: {
                type: 'string',
                nullable: false
            },

            goal: {
                type: 'string',
                nullable: false
            },

            status: {
                type: 'string',
                nullable: false,
                isValidExperimentStatus: true
            },

            description: {
                type: 'string'
            }
        });

        createExperimentSchema.setDefaults({
            aim: '',
            goal: '',
            description: '',
            status: 'active'
        });
        createExperimentSchema.validateAsync = promise.promisify(createExperimentSchema.validate);
        return createExperimentSchema;
    }

    function defineUpdateExperimentSchema() {
        let updateExperimentSchema = schema.defineSchema('UpdateExperiment', {
            name: {
                type: 'string',
                nullable: true
            },

            description: {
                type: 'string',
                nullable: true
            },

            note: {
                type: 'string',
                nullable: true
            },

            goals: {
                type: 'array',
                nullable: true
            },

            collaborators: {
                type: 'array',
                nullable: true
            },

            funding: {
                type: 'array',
                nullable: true
            },

            citations: {
                type: 'array',
                nullable: true
            },

            papers: {
                type: 'array',
                nullable: true
            },

            publications: {
                type: 'array',
                nullable: true
            },

            status: {
                type: 'string',
                nullable: true
            }
        });
        updateExperimentSchema.setDefaults({});
        updateExperimentSchema.validateAsync = promise.promisify(updateExperimentSchema.validate);
        return updateExperimentSchema;
    }

    function defineCreateExperimentTaskSchema() {
        let createExperimentTaskSchema = schema.defineSchema('CreateExperimentTask', {
            name: {
                type: 'string',
                nullable: false
            },
            note: {
                type: 'string',
                nullable: false
            },
            parent_id: {
                type: 'string',
                nullable: false
            },
            index: {
                type: 'integer',
                nullable: false,
                min: 0
            }
        });

        createExperimentTaskSchema.setDefaults({parent_id: '', note: ''});
        createExperimentTaskSchema.validateAsync = promise.promisify(createExperimentTaskSchema.validate);
        return createExperimentTaskSchema;
    }

    function defineUpdateExperimentTaskSchema() {
        let updateExperimentTaskSchema = schema.defineSchema('UpdateExperimentTask', {
            name: {
                type: 'string',
                nullable: true
            },
            note: {
                type: 'string',
                nullable: true
            },
            parent_id: {
                type: 'string',
                nullable: true
            },
            flags: {
                nullable: true,
                flagged: {
                    type: 'boolean',
                    nullable: true
                },
                done: {
                    type: 'boolean',
                    nullable: true
                },
                starred: {
                    type: 'boolean',
                    nullable: true
                }
            },

            swap: {
                nullable: true,
                task_id: {
                    type: 'string',
                    nullable: true
                }
            }
        });

        updateExperimentTaskSchema.setDefaults({parent_id: ''});
        updateExperimentTaskSchema.validateAsync = promise.promisify(updateExperimentTaskSchema.validate);
        return updateExperimentTaskSchema;
    }

    function defineCreateExperimentNoteSchema() {
        let createExperimentNoteSchema = schema.defineSchema('CreateExperimentNote', {
            name: {
                type: 'string',
                nullable: false
            },
            note: {
                type: 'string',
                nullable: false
            }
        });

        createExperimentNoteSchema.setDefaults({});
        createExperimentNoteSchema.validateAsync = promise.promisify(createExperimentNoteSchema.validate);
        return createExperimentNoteSchema;
    }

    function defineUpdateExperimentNoteSchema() {
        let updateExperimentNoteSchema = schema.defineSchema('UpdateExperimentNote', {
            name: {
                type: 'string'
            },
            note: {
                type: 'string'
            }
        });

        updateExperimentNoteSchema.setDefaults({});
        updateExperimentNoteSchema.validateAsync = promise.promisify(updateExperimentNoteSchema.validate);
        return updateExperimentNoteSchema;
    }

    function defineUpdateExperimentTaskTemplatePropsSchema() {
        let updateExperimentTaskTemplatePropsSchema = schema.defineSchema('updateExperimentTaskTemplatePropsSchema', {
            properties: {
                type: 'array',
                nullable: false
            },

            template_id: {
                type: 'string',
                nullable: false
            }
        });
        updateExperimentTaskTemplatePropsSchema.setDefaults({});
        updateExperimentTaskTemplatePropsSchema.validateAsync = promise.promisify(updateExperimentTaskTemplatePropsSchema.validate);
        return updateExperimentTaskTemplatePropsSchema;
    }

    function defineTemplatePropertySchema() {
        let templatePropertySchema = schema.defineSchema('templatePropertySchema', {
            id: {
                type: 'string',
                nullable: false
            },

            otype: {
                type: 'string',
                nullable: false
            },

            setup_attribute: {
                type: 'string',
                nullable: false
            },

            setup_id: {
                type: 'string',
                nullable: false
            },

            attribute: {
                type: 'string',
                nullable: false
            },

            description: {
                type: 'string',
                nullable: false
            },

            name: {
                type: 'string',
                nullable: false
            },

            unit: {
                type: 'string',
                nullable: true
            },

            value: {
                nullable: false
            }
        });

        templatePropertySchema.setDefaults({
            unit: ''
        });
        templatePropertySchema.validateAsync = promise.promisify(templatePropertySchema.validate);
        return templatePropertySchema;
    }

    function defineTemplateCommandSchema() {
        let templateCommandSchema = schema.defineSchema('templateCommandSchema', {
            id: {
                type: 'string',
                nullable: false
            },

            property_set_id: {
                type: 'string',
                nullable: true
            },

            command: {
                type: 'string',
                nullable: false
            }
        });

        templateCommandSchema.setDefaults({
            property_set_id: ''
        });
        templateCommandSchema.validateAsync = promise.promisify(templateCommandSchema.validate);
        return templateCommandSchema;
    }
};
<|endoftext|>


<commit_before>const getWorkloadPoints = require('./data/get-workload-points')
const updatePreviousWorkloadPointsEffectiveTo = require('./data/update-workload-points-effective-to')
const insertNewWorkloadPoints = require('./data/insert-workload-points')
const getWorkloadIdsForWpRecalc = require('./data/get-ids-for-workload-points-recalc')
const createCalculateWorkloadPointsTask = require('./data/create-calculate-workload-points-task')
const Link = require('./domain/link')
const dateFormatter = require('./date-formatter')
const userRoleService = require('../services/user-role-service')

module.exports.getWorkloadPoints = function (isT2A) {
  var result = {}

  var breadcrumbs = [
    new Link('Workload Points', '/admin/workload-points'),
    new Link('Admin', '/admin')
  ]

  return getWorkloadPoints(isT2A).then(function (workloadPoints) {
    var userId = null
    if (workloadPoints !== undefined) {
      var formattedUpdateDate = dateFormatter.formatDate(workloadPoints.effectiveFrom, 'DD/MM/YYYY HH:mm')
      workloadPoints.effectiveFrom = formattedUpdateDate
      userId = workloadPoints.updatedByUserId
    }
    if (isT2A) {
      breadcrumbs[0].title = breadcrumbs[0].title + ' (T2A)'
    }
    return userRoleService.getUserById(userId)
    .then(function (user) {
      var updatedBy = userId // Default to the user id
      var title = breadcrumbs[0].title
      if (user) {
        updatedBy = (user.name) ? user.name : user.username
      }
      result.title = title
      result.subTitle = breadcrumbs[1].title
      result.workloadPoints = workloadPoints
      result.updatedBy = updatedBy
      result.breadcrumbs = breadcrumbs
      return result
    })
  })
}

module.exports.updateWorkloadPoints = function (workloadPoints, isT2A = false) {
  return updatePreviousWorkloadPointsEffectiveTo(workloadPoints.previousWpId).then(function (updateResults) {
    return insertNewWorkloadPoints(workloadPoints).then(function (insertResults) {
      return getWorkloadIdsForWpRecalc(workloadPoints.previousWpId, isT2A).then(function (ids) {
        return createCalculateWorkloadPointsTask(ids.minWorkloadStagingId, ids.workloadReportId, (ids.maxWorkloadStagingId - ids.minWorkloadStagingId + 1))
        .then(function (taskResults) {
          return taskResults
        })
      })
    })
  })
}
<commit_msg>Make code more concise<commit_after>const getWorkloadPoints = require('./data/get-workload-points')
const updatePreviousWorkloadPointsEffectiveTo = require('./data/update-workload-points-effective-to')
const insertNewWorkloadPoints = require('./data/insert-workload-points')
const getWorkloadIdsForWpRecalc = require('./data/get-ids-for-workload-points-recalc')
const createCalculateWorkloadPointsTask = require('./data/create-calculate-workload-points-task')
const Link = require('./domain/link')
const dateFormatter = require('./date-formatter')
const userRoleService = require('../services/user-role-service')

module.exports.getWorkloadPoints = function (isT2A) {
  var result = {}

  var breadcrumbs = [
    new Link('Workload Points', '/admin/workload-points'),
    new Link('Admin', '/admin')
  ]

  return getWorkloadPoints(isT2A).then(function (workloadPoints) {
    var userId = null
    if (workloadPoints !== undefined) {
      var formattedUpdateDate = dateFormatter.formatDate(workloadPoints.effectiveFrom, 'DD/MM/YYYY HH:mm')
      workloadPoints.effectiveFrom = formattedUpdateDate
      userId = workloadPoints.updatedByUserId
    }
    if (isT2A) {
      breadcrumbs[0].title += ' (T2A)'
    }
    return userRoleService.getUserById(userId)
    .then(function (user) {
      var updatedBy = userId // Default to the user id
      var title = breadcrumbs[0].title
      if (user) {
        updatedBy = (user.name) ? user.name : user.username
      }
      result.title = title
      result.subTitle = breadcrumbs[1].title
      result.workloadPoints = workloadPoints
      result.updatedBy = updatedBy
      result.breadcrumbs = breadcrumbs
      return result
    })
  })
}

module.exports.updateWorkloadPoints = function (workloadPoints, isT2A = false) {
  return updatePreviousWorkloadPointsEffectiveTo(workloadPoints.previousWpId).then(function (updateResults) {
    return insertNewWorkloadPoints(workloadPoints).then(function (insertResults) {
      return getWorkloadIdsForWpRecalc(workloadPoints.previousWpId, isT2A).then(function (ids) {
        return createCalculateWorkloadPointsTask(ids.minWorkloadStagingId, ids.workloadReportId, (ids.maxWorkloadStagingId - ids.minWorkloadStagingId + 1))
        .then(function (taskResults) {
          return taskResults
        })
      })
    })
  })
}
<|endoftext|>