<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() {

/* jshint newcap: false */

"use strict";

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

function TableLayout(items, attrs, $scope, $compile) {

	var colWidths = attrs.widths,
		numCols = +attrs.cols || 4,
		curCol = 0,
		layout = [[]];

	function add(item, label) {

		if (item.is('br')) {
			curCol = 0;
			item.hide();
			return layout.push([]);
		}

		var row = _.last(layout),
			cell = null,
			colspan = +item.attr('x-colspan') || 1,
			rowspan = +item.attr('x-rowspan') || 1;

		if (curCol + colspan >= numCols + 1) {
			curCol = 0, row = [];
			layout.push(row);
		}

		if (label) {
			cell = {};
			cell.elem = label;
			cell.css = label.attr('x-cell-css');
			row.push(cell);
			if (rowspan > 1) cell.rowspan = rowspan;
			if (colspan > 1) colspan -= 1;
			curCol += 1;
		}

		cell = {};
		cell.elem = item;
		cell.css = item.attr('x-cell-css');
		if (colspan > 1) cell.colspan = colspan;
		if (rowspan > 1) cell.rowspan = rowspan;

		row.push(cell);
		curCol += colspan;
	}

	if (colWidths && angular.isString(colWidths)) {
		colWidths = colWidths.trim().split(/\s*,\s*/);
		for(var i = 0 ; i < colWidths.length; i++) {
			var width = colWidths[i];
			if (/^(\d+)$/.test(width)) width = width + 'px';
			if (width == '*') width = 'auto';
			colWidths[i] = width;
		}
	}

	items.each(function(){
		var el = $(this),
			title = el.attr('x-title'),
			noTitle = el.attr('x-show-title') == 'false';

		var labelScope = el.data('$scope');
		if (labelScope) {
			labelScope = labelScope.$new();
		}

		if (numCols > 1 && !noTitle && title) {
			var label = $('').html(title).attr('x-for-widget', el.attr('id')),
				labelElem = $compile(label)(labelScope || $scope);
			el.data('label', labelElem);
			return add(el, labelElem);
		}
		add(el);
	});

	var table = $('
</table');

	function isLabel(cell) {
		return cell.css === "form-label" || (cell.elem && cell.elem.is('label,.spacer-item'));
	}

	function computeWidths(row) {
		if (row.length === 1) return null;
		var widths = [],
			labelCols = 0,
			itemCols = 0,
			emptyCols = 0;

		_.each(row, function(cell) {
			if (isLabel(cell)) {
				labelCols += (cell.colspan || 1);
			} else {
				itemCols += (cell.colspan || 1);
			}
		});

		emptyCols = numCols - (labelCols + itemCols);

		labelCols += (emptyCols / 2);
		itemCols += (emptyCols / 2) + (emptyCols % 2);

		var labelWidth = labelCols ? Math.min(50, (12 * labelCols)) / labelCols : 0;
		var itemWidth = (100 - (labelWidth * labelCols)) / itemCols;

		_.each(row, function(cell, i) {
			var width = ((isLabel(cell) ? labelWidth : itemWidth) * (cell.colspan || 1));
			widths[i] = width + "%";
		});

		return widths;
	}

	_.each(layout, function(row){
		var tr = $('

'),
			numCells = 0,
			widths = colWidths || computeWidths(row);

		_.each(row, function(cell, i) {
				var el = $('	')
					.addClass(cell.css)
					.attr('colspan', cell.colspan)
					.attr('rowspan', cell.rowspan)
					.append(cell.elem)
					.appendTo(tr);
				if (_.isArray(widths) && widths[i]) {
					el.width(widths[i]);
				}
				if ($(cell.elem).is('.form-item-container') && axelor.config['view.form.hot-edit']) {
					$(cell.elem).prepend($(''));
				}
				numCells += cell.colspan || 1;
		});

		// append remaining cells
		for (var i = numCells ; i < numCols ; i++) {
			$('	').appendTo(tr).width((widths||[])[i]);
		}

		tr.appendTo(table);
	});

	return table;
} //- TableLayout

ui.directive('uiTableLayout', ['$compile', function($compile) {

	return function(scope, element, attrs) {
		var elem = attrs.layoutSelector ? element.find(attrs.layoutSelector) : element;
		var items = elem.children();

		var layout = TableLayout(items, attrs, scope, $compile);
		var brTags = element.children('br:hidden'); // detach all the
 tags

		scope.$on('$destroy', function(){
			brTags.remove();
		});

		elem.append(layout);
	};

}]);

function PanelLayout(items, attrs, $scope, $compile) {

	var stacked = attrs.stacked || false,
		numCols = 12,
		numSpan = +(attrs.itemSpan) || 6,
		curCol = 0,
		layout = [$('
')];

	function add(item, label) {
		var row = _.last(layout),
			cell = $('
'),
			span = +item.attr('x-colspan') || numSpan,
			offset = +item.attr('x-coloffset') || 0;

		span = Math.min(span, numCols);
		if (stacked) {
			span = 0;
		}

		if (item.is('.spacer-item')) {
			curCol += (span + offset);
			item.remove();
			return;
		}

		if (curCol + (span + offset) >= numCols + 1 && !stacked) {
			curCol = 0, row = $('
');
			layout.push(row);
		}
		if (label) {
			label.appendTo(cell);
			row.addClass('has-labels');
		}

		cell.addClass(item.attr('x-cell-css'));

		if (span) {
			cell.addClass('span' + span);
		}
		if (offset) {
			cell.addClass('offset' + offset);
		}

		cell.append(item);
		cell.appendTo(row);

		curCol += (span + offset);
	}

	items.each(function (item, i) {
		var el = $(this),
			title = el.attr('x-title'),
			noTitle = el.attr('x-show-title') == 'false';

		var labelScope = el.data('$scope');
		if (labelScope) {
			labelScope = labelScope.$new();
		}

		if (!noTitle && title) {
			var label = $('').html(title).attr('x-for-widget', el.attr('id')),
				labelElem = $compile(label)(labelScope || $scope);
			el.data('label', labelElem);
			return add(el, labelElem);
		}
		add(el);
	});

	var container = $('
</div').append(layout);

	return container;
}

ui.directive('uiPanelLayout', ['$compile', function($compile) {

	return {
		priority: 1000,
		link: function(scope, element, attrs) {
			var elem = element.children('[ui-transclude]:first');
			var items = elem.children();
			var layout = PanelLayout(items, attrs, scope, $compile);
			elem.append(layout);
		}
	};

}]);

function BarLayout(items, attrs, $scope, $compile) {

	var main = $('
');
	var side = $('
');

	items.each(function(item, i) {
		var elem = $(this);
		var prop = elem.scope().field || {};
		if (elem.attr('x-sidebar')) {
			elem.appendTo(side);
		} else {
			elem.appendTo(main);
		}
		if (prop.attached) {
			elem.addClass("attached");
		}
	});

	if (side.children().size() === 0) {
		main.removeClass("span8").addClass("span12");
		side = null;
	}

	var row = $('
').append(main);

	if (side) {
		side.appendTo(row);
	}

	return row;
}

ui.directive('uiBarLayout', ['$compile', function($compile) {

	return function(scope, element, attrs) {
		var items = element.children();
		var layout = BarLayout(items, attrs, scope, $compile);
		var schema = scope.schema || {};
		var css = null;

		scope._isPanelForm = true;

		element.append(layout);
		element.addClass('bar-layout');

		if (element.has('[x-sidebar]').size() === 0) {
			css = "mid";
		}
		if (element.is('form') && ["mini", "mid", "large"].indexOf(schema.width) > -1) {
			css = scope.schema.width;
		}
		if (css) {
			element.addClass(css + '-form');
		}
	};
}]);

ui.directive('uiPanelEditor', ['$compile', 'ActionService', function($compile, ActionService) {

	return {
		scope: true,
		link: function(scope, element, attrs) {
			var field = scope.field;
			var editor = field.editor;

			if (!editor) {
				return;
			}

			var items = editor.items || [];
			var widths = _.map(items, function (item) {
				item.showTitle = item.showTitle === true || (editor.widgetAttrs||{}).showTitles !== "false";
				if (!item.showTitle) {
					item.placeholder = item.placeholder || item.title || item.autoTitle;
				}
				if (editor.itemSpan && !item.colSpan) {
					item.colSpan = editor.itemSpan;
				}
				var width = item.width || (item.widgetAttrs||{}).width;
				return width ? width : (item.widget === 'toggle' ? 24 : '*');
			});

			var schema = {
				cols: items.length,
				colWidths: widths.join(','),
				items: items
			};

			if (editor.layout !== 'table') {
				schema = {
					items: [{
						type: 'panel',
						items: items
					}]
				};
			}

			scope.fields = editor.fields || scope.fields;

			var form = ui.formBuild(scope, schema, scope.fields);
			var isRelational = /-to-one$/.test(field.type);

			if (isRelational) {
				Object.defineProperty(scope, 'record', {
					enumerable: true,
					get: function () {
						return (scope.$parent.record||{})[field.name];
					},
					set: function (value) {
						scope.setValue(value, true);
					}
				});
				Object.defineProperty(scope, '$$original', {
					enumerable: true,
					get: function () {
						return (scope.$parent.$$original||{})[field.name];
					},
					set: function (value) {}
				});
				// make sure to fetch missing values
				scope.$watch('record.id', function (value, old) {
					var ds = scope._dataSource;
					var record = scope.record;
					if (value <= 0 || !value || record.$fetched || record.$fetchedRelated) {
						return;
					}
					var missing = _.filter(_.keys(editor.fields), function (name) {
						return !record.hasOwnProperty(name);
					});
					if (missing.length === 0) {
						return;
					}
					record.$fetchedRelated = true;
					return ds.read(value, {fields: missing}).success(function(rec) {
						var values = _.pick(rec, missing);
						record = _.extend(record, values);
					});
				});
			}

			if (field.target) {
				scope.getContext = function () {
					var context = _.extend({}, scope.record);
					context._model = scope._model;
					context._parent = scope.$parent.getContext();
					return context;
				};
				// make sure to trigger record-change with proper record data
				scope.$watch('record', function (value, old) {
					if (value && value !== old) {
						value.$changed = true;
						value.version = _.isNumber(value.version) ? value.version : value.$version;
					}
					scope.$broadcast("on:record-change", value);
				}, true);
				scope.$timeout(function () {
					scope.$broadcast("on:record-change", scope.record);
				});
			}

			form = $compile(form)(scope);
			form.children('div.row').removeClass('row').addClass('row-fluid');
			element.append(form);

			if (field.target) {
				var handler = null;
				if (editor.onNew) {
					schema.onNew = editor.onNew;
					form.data('$editorForm', form);
					handler = ActionService.handler(scope, form, {
						action: editor.onNew
					});
				}
				scope.$watch('record.id', function (value, old) {
					if (!value && handler) {
						handler.onNew();
					}
				});
			}

			scope.isValid = function () {
				return scope.form && scope.form.$valid;
			};

			function isEmpty(record) {
				if (!record || _.isEmpty(record)) return true;
				var values = _.filter(record, function (value, name) {
					return !(/[\$_]/.test(name) || value === null || value === undefined);
				});
				return values.length === 0;
			}

			scope.$watch(function () {
				if (isRelational && editor.showOnNew === false && !scope.canShowEditor()) {
					return;
				}
				var valid = scope.isValid();
				if (!valid && !scope.$parent.isRequired() && isEmpty(scope.record)) {
					valid = true;
				}
				if (scope.setValidity) {
					scope.setValidity('valid', valid);
					element.toggleClass('nested-not-required', valid);
				} else {
					scope.$parent.form.$setValidity('valid', valid, scope.form);
				}
			});

			scope.$on('$destroy', function () {
				if (scope.setValidity) {
					scope.setValidity('valid', true);
				}
			});
		}
	};
}]);

})();
<commit_msg>Fix showTitle attribute issue on editor fields<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() {

/* jshint newcap: false */

"use strict";

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

function TableLayout(items, attrs, $scope, $compile) {

	var colWidths = attrs.widths,
		numCols = +attrs.cols || 4,
		curCol = 0,
		layout = [[]];

	function add(item, label) {

		if (item.is('br')) {
			curCol = 0;
			item.hide();
			return layout.push([]);
		}

		var row = _.last(layout),
			cell = null,
			colspan = +item.attr('x-colspan') || 1,
			rowspan = +item.attr('x-rowspan') || 1;

		if (curCol + colspan >= numCols + 1) {
			curCol = 0, row = [];
			layout.push(row);
		}

		if (label) {
			cell = {};
			cell.elem = label;
			cell.css = label.attr('x-cell-css');
			row.push(cell);
			if (rowspan > 1) cell.rowspan = rowspan;
			if (colspan > 1) colspan -= 1;
			curCol += 1;
		}

		cell = {};
		cell.elem = item;
		cell.css = item.attr('x-cell-css');
		if (colspan > 1) cell.colspan = colspan;
		if (rowspan > 1) cell.rowspan = rowspan;

		row.push(cell);
		curCol += colspan;
	}

	if (colWidths && angular.isString(colWidths)) {
		colWidths = colWidths.trim().split(/\s*,\s*/);
		for(var i = 0 ; i < colWidths.length; i++) {
			var width = colWidths[i];
			if (/^(\d+)$/.test(width)) width = width + 'px';
			if (width == '*') width = 'auto';
			colWidths[i] = width;
		}
	}

	items.each(function(){
		var el = $(this),
			title = el.attr('x-title'),
			noTitle = el.attr('x-show-title') == 'false';

		var labelScope = el.data('$scope');
		if (labelScope) {
			labelScope = labelScope.$new();
		}

		if (numCols > 1 && !noTitle && title) {
			var label = $('').html(title).attr('x-for-widget', el.attr('id')),
				labelElem = $compile(label)(labelScope || $scope);
			el.data('label', labelElem);
			return add(el, labelElem);
		}
		add(el);
	});

	var table = $('
</table');

	function isLabel(cell) {
		return cell.css === "form-label" || (cell.elem && cell.elem.is('label,.spacer-item'));
	}

	function computeWidths(row) {
		if (row.length === 1) return null;
		var widths = [],
			labelCols = 0,
			itemCols = 0,
			emptyCols = 0;

		_.each(row, function(cell) {
			if (isLabel(cell)) {
				labelCols += (cell.colspan || 1);
			} else {
				itemCols += (cell.colspan || 1);
			}
		});

		emptyCols = numCols - (labelCols + itemCols);

		labelCols += (emptyCols / 2);
		itemCols += (emptyCols / 2) + (emptyCols % 2);

		var labelWidth = labelCols ? Math.min(50, (12 * labelCols)) / labelCols : 0;
		var itemWidth = (100 - (labelWidth * labelCols)) / itemCols;

		_.each(row, function(cell, i) {
			var width = ((isLabel(cell) ? labelWidth : itemWidth) * (cell.colspan || 1));
			widths[i] = width + "%";
		});

		return widths;
	}

	_.each(layout, function(row){
		var tr = $('

'),
			numCells = 0,
			widths = colWidths || computeWidths(row);

		_.each(row, function(cell, i) {
				var el = $('	')
					.addClass(cell.css)
					.attr('colspan', cell.colspan)
					.attr('rowspan', cell.rowspan)
					.append(cell.elem)
					.appendTo(tr);
				if (_.isArray(widths) && widths[i]) {
					el.width(widths[i]);
				}
				if ($(cell.elem).is('.form-item-container') && axelor.config['view.form.hot-edit']) {
					$(cell.elem).prepend($(''));
				}
				numCells += cell.colspan || 1;
		});

		// append remaining cells
		for (var i = numCells ; i < numCols ; i++) {
			$('	').appendTo(tr).width((widths||[])[i]);
		}

		tr.appendTo(table);
	});

	return table;
} //- TableLayout

ui.directive('uiTableLayout', ['$compile', function($compile) {

	return function(scope, element, attrs) {
		var elem = attrs.layoutSelector ? element.find(attrs.layoutSelector) : element;
		var items = elem.children();

		var layout = TableLayout(items, attrs, scope, $compile);
		var brTags = element.children('br:hidden'); // detach all the
 tags

		scope.$on('$destroy', function(){
			brTags.remove();
		});

		elem.append(layout);
	};

}]);

function PanelLayout(items, attrs, $scope, $compile) {

	var stacked = attrs.stacked || false,
		numCols = 12,
		numSpan = +(attrs.itemSpan) || 6,
		curCol = 0,
		layout = [$('
')];

	function add(item, label) {
		var row = _.last(layout),
			cell = $('
'),
			span = +item.attr('x-colspan') || numSpan,
			offset = +item.attr('x-coloffset') || 0;

		span = Math.min(span, numCols);
		if (stacked) {
			span = 0;
		}

		if (item.is('.spacer-item')) {
			curCol += (span + offset);
			item.remove();
			return;
		}

		if (curCol + (span + offset) >= numCols + 1 && !stacked) {
			curCol = 0, row = $('
');
			layout.push(row);
		}
		if (label) {
			label.appendTo(cell);
			row.addClass('has-labels');
		}

		cell.addClass(item.attr('x-cell-css'));

		if (span) {
			cell.addClass('span' + span);
		}
		if (offset) {
			cell.addClass('offset' + offset);
		}

		cell.append(item);
		cell.appendTo(row);

		curCol += (span + offset);
	}

	items.each(function (item, i) {
		var el = $(this),
			title = el.attr('x-title'),
			noTitle = el.attr('x-show-title') == 'false';

		var labelScope = el.data('$scope');
		if (labelScope) {
			labelScope = labelScope.$new();
		}

		if (!noTitle && title) {
			var label = $('').html(title).attr('x-for-widget', el.attr('id')),
				labelElem = $compile(label)(labelScope || $scope);
			el.data('label', labelElem);
			return add(el, labelElem);
		}
		add(el);
	});

	var container = $('
</div').append(layout);

	return container;
}

ui.directive('uiPanelLayout', ['$compile', function($compile) {

	return {
		priority: 1000,
		link: function(scope, element, attrs) {
			var elem = element.children('[ui-transclude]:first');
			var items = elem.children();
			var layout = PanelLayout(items, attrs, scope, $compile);
			elem.append(layout);
		}
	};

}]);

function BarLayout(items, attrs, $scope, $compile) {

	var main = $('
');
	var side = $('
');

	items.each(function(item, i) {
		var elem = $(this);
		var prop = elem.scope().field || {};
		if (elem.attr('x-sidebar')) {
			elem.appendTo(side);
		} else {
			elem.appendTo(main);
		}
		if (prop.attached) {
			elem.addClass("attached");
		}
	});

	if (side.children().size() === 0) {
		main.removeClass("span8").addClass("span12");
		side = null;
	}

	var row = $('
').append(main);

	if (side) {
		side.appendTo(row);
	}

	return row;
}

ui.directive('uiBarLayout', ['$compile', function($compile) {

	return function(scope, element, attrs) {
		var items = element.children();
		var layout = BarLayout(items, attrs, scope, $compile);
		var schema = scope.schema || {};
		var css = null;

		scope._isPanelForm = true;

		element.append(layout);
		element.addClass('bar-layout');

		if (element.has('[x-sidebar]').size() === 0) {
			css = "mid";
		}
		if (element.is('form') && ["mini", "mid", "large"].indexOf(schema.width) > -1) {
			css = scope.schema.width;
		}
		if (css) {
			element.addClass(css + '-form');
		}
	};
}]);

ui.directive('uiPanelEditor', ['$compile', 'ActionService', function($compile, ActionService) {

	return {
		scope: true,
		link: function(scope, element, attrs) {
			var field = scope.field;
			var editor = field.editor;

			if (!editor) {
				return;
			}

			var items = editor.items || [];
			var widths = _.map(items, function (item) {
				if (item.showTitle === undefined) {
					item.showTitle = (editor.widgetAttrs||{}).showTitles !== "false";
				}
				if (!item.showTitle) {
					item.placeholder = item.placeholder || item.title || item.autoTitle;
				}
				if (editor.itemSpan && !item.colSpan) {
					item.colSpan = editor.itemSpan;
				}
				var width = item.width || (item.widgetAttrs||{}).width;
				return width ? width : (item.widget === 'toggle' ? 24 : '*');
			});

			var schema = {
				cols: items.length,
				colWidths: widths.join(','),
				items: items
			};

			if (editor.layout !== 'table') {
				schema = {
					items: [{
						type: 'panel',
						items: items
					}]
				};
			}

			scope.fields = editor.fields || scope.fields;

			var form = ui.formBuild(scope, schema, scope.fields);
			var isRelational = /-to-one$/.test(field.type);

			if (isRelational) {
				Object.defineProperty(scope, 'record', {
					enumerable: true,
					get: function () {
						return (scope.$parent.record||{})[field.name];
					},
					set: function (value) {
						scope.setValue(value, true);
					}
				});
				Object.defineProperty(scope, '$$original', {
					enumerable: true,
					get: function () {
						return (scope.$parent.$$original||{})[field.name];
					},
					set: function (value) {}
				});
				// make sure to fetch missing values
				scope.$watch('record.id', function (value, old) {
					var ds = scope._dataSource;
					var record = scope.record;
					if (value <= 0 || !value || record.$fetched || record.$fetchedRelated) {
						return;
					}
					var missing = _.filter(_.keys(editor.fields), function (name) {
						return !record.hasOwnProperty(name);
					});
					if (missing.length === 0) {
						return;
					}
					record.$fetchedRelated = true;
					return ds.read(value, {fields: missing}).success(function(rec) {
						var values = _.pick(rec, missing);
						record = _.extend(record, values);
					});
				});
			}

			if (field.target) {
				scope.getContext = function () {
					var context = _.extend({}, scope.record);
					context._model = scope._model;
					context._parent = scope.$parent.getContext();
					return context;
				};
				// make sure to trigger record-change with proper record data
				scope.$watch('record', function (value, old) {
					if (value && value !== old) {
						value.$changed = true;
						value.version = _.isNumber(value.version) ? value.version : value.$version;
					}
					scope.$broadcast("on:record-change", value);
				}, true);
				scope.$timeout(function () {
					scope.$broadcast("on:record-change", scope.record);
				});
			}

			form = $compile(form)(scope);
			form.children('div.row').removeClass('row').addClass('row-fluid');
			element.append(form);

			if (field.target) {
				var handler = null;
				if (editor.onNew) {
					schema.onNew = editor.onNew;
					form.data('$editorForm', form);
					handler = ActionService.handler(scope, form, {
						action: editor.onNew
					});
				}
				scope.$watch('record.id', function (value, old) {
					if (!value && handler) {
						handler.onNew();
					}
				});
			}

			scope.isValid = function () {
				return scope.form && scope.form.$valid;
			};

			function isEmpty(record) {
				if (!record || _.isEmpty(record)) return true;
				var values = _.filter(record, function (value, name) {
					return !(/[\$_]/.test(name) || value === null || value === undefined);
				});
				return values.length === 0;
			}

			scope.$watch(function () {
				if (isRelational && editor.showOnNew === false && !scope.canShowEditor()) {
					return;
				}
				var valid = scope.isValid();
				if (!valid && !scope.$parent.isRequired() && isEmpty(scope.record)) {
					valid = true;
				}
				if (scope.setValidity) {
					scope.setValidity('valid', valid);
					element.toggleClass('nested-not-required', valid);
				} else {
					scope.$parent.form.$setValidity('valid', valid, scope.form);
				}
			});

			scope.$on('$destroy', function () {
				if (scope.setValidity) {
					scope.setValidity('valid', true);
				}
			});
		}
	};
}]);

})();
<|endoftext|>


<commit_before>/*
 * Axelor Business Solutions
 *
 * Copyright (C) 2005-2019 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');

/**
 * The Form widget.
 *
 */
ui.formWidget('Form', {

  priority: 100,

  css: "dynamic-form",

  scope: false,

  compile: function(element, attrs) {

    element.hide();
    element.find('[x-field],[data-field]').each(function(){

      var elem = $(this),
        name = elem.attr('x-field') || elem.attr('data-field');

      if (name && elem.attr('ui-button') === undefined) {
        if (!elem.attr('ng-model')) {
          elem.attr('ng-model', 'record.' + name);
        }
        if (!elem.attr('ng-required')) {
          // always attache a required validator to make
          // dynamic `required` attribute change effective
          elem.attr('ng-required', false);
        }
      }
    });

    return ui.formCompile.apply(this, arguments);
  },

  link: function(scope, element, attrs, controller) {

    element.on('submit', function(e) {
      e.preventDefault();
    });

    scope.$watch('record', function formRecordWatch(rec, old) {
      if (element.is(':visible')) {
        return;
      }
      scope.ajaxStop(function() {
        element.show();
        axelor.$adjustSize();
      });
    });
  }
});

/**
 * This directive is used filter $watch on scopes of inactive tabs.
 *
 */
ui.directive('uiTabGate', function() {

  return {

    compile: function compile(tElement, tAttrs) {

      return {
        pre: function preLink(scope, element, attrs) {
          scope.$watchChecker(function(current) {
            if (current.tabSelected === undefined) {
              return !scope.tab || scope.tab.selected === undefined || scope.tab.selected;
            }
            return current.tabSelected;
          });
        }
      };
    }
  };
});

/**
 * This directive is used to filter $watch on scopes of hidden forms.
 *
 */
ui.directive('uiFormGate', function() {

  return {
    compile: function compile(tElement, tAttrs) {

      return {
        pre: function preLink(scope, element, attrs) {
          var parent = null;
          scope.$watchChecker(function(current) {
            if (scope.tabSelected === false) {
              return false;
            }
            if (parent === null) {
              parent = element.parents('[ui-show]:first');
            }
            // hack for hidden nested editors (#2173)
            if (scope.$$forceWatch) {
              return true;
            }
            return !(parent.hasClass('ui-hide') || parent.hasClass('ui-hide'));
          });
        }
      };
    }
  };
});

/**
 * This directive is used to filter $watch on scopes based on some condition.
 *
 */
ui.directive('uiWatchIf', ['$parse', function($parse) {

  return {
    compile: function compile(tElement, tAttrs) {
      return {
        pre: function preLink(scope, element, attrs) {
          var value = false,
            expression = $parse(attrs.uiWatchIf);

          scope.$watchChecker(function (current) {
            if (current === scope) {
              return value = expression(scope);
            }
            return value;
          });
        }
      };
    }
  };
}]);

function toBoolean(value) {
  if (value && value.length !== 0) {
    var v = angular.lowercase("" + value);
    value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]');
  } else {
    value = false;
  }
  return value;
}

/**
 * This directive is used to speedup uiFormGate.
 */
ui.directive('uiShow', function() {

  return {
    scope: true, // create new scope to always watch the expression
    link: function link(scope, element, attrs) {
      scope.$$shouldWatch = true;
      scope.$watch(attrs.uiShow, function uiShowWatchAction(value){
        var val = toBoolean(value);
        element.css({ display: val ? '' : 'none', opacity: 0 }).toggleClass('ui-hide', !val);
        if (val) {
          element.animate({ opacity: 1 }, 300);
        }
      });
    }
  };
});

/**
 * This directive is used by view-pane to attach/detach element from DOM tree
 */
ui.directive('uiAttach', function () {
  return function (scope, element, attrs) {
    var parent = null;
    var uiAttachWatch = function uiAttachWatch(attach) {
      var result = toBoolean(attach);
      if (result) {
        if (parent) {
          element.appendTo(parent);
          parent = null;
          scope.$broadcast('dom:attach');
        }
      } else {
        parent = element.parent();
        scope.$broadcast('dom:detach');
        setTimeout(function () {
          element.detach();
        }, 100);
      }
    };

    uiAttachWatch.uiAttachWatch = true;

    scope.$watch(attrs.uiAttach, uiAttachWatch, true);
    scope.$on('$destroy', function () {
      if (parent) {
        parent = null;
        element.remove();
      }
    });
  };
});

/**
 * This directive can be used by widget to restore scroll when element is re-attached to DOM tree.
 */
ui.directive('uiAttachScroll', function () {
  return function (scope, element, attrs) {
    setTimeout(function () {
      var elem = element;
      var scrollTop = 0;

      if (attrs.uiAttachScroll) {
        elem = element.find(attrs.uiAttachScroll);
      }

      elem.on('scroll', function () {
        scrollTop = this.scrollTop;
      });

      function resetScroll() {
        elem.scrollTop(scrollTop);
      }

      scope.$on('dom:attach', resetScroll);
      scope.$on('tab:select', resetScroll);
    }, 300);
  };
});

ui.directive('uiWidgetStates', ['$parse', '$interpolate', function($parse, $interpolate) {

  function isValid(scope, name) {
    if (!name) return scope.isValid();
    var ctrl = scope.form;
    if (ctrl) {
      ctrl = ctrl[name];
    }
    if (ctrl) {
      return ctrl.$valid;
    }
  }

  function withContext(scope, record) {
    var values = _.extend({}, scope._context, scope._jsonContext, record);
    return _.extend(values, {
      $user: axelor.config['user.login'],
      $group: axelor.config['user.group'],
      $userId: axelor.config['user.id'],
    });
  }

  function handleCondition(scope, field, attr, condition, negative) {

    if (!condition || _.isBoolean(condition)) {
      return;
    }

    scope.$on("on:record-change", function(e, rec, force) {
      if (field && field.jsonField) {
        handle(scope.record);
      } else if (rec === scope.record || force) {
        handle(rec);
      }
    });
    scope.$on("on:grid-selection-change", function(e, context) {
      if (field && field.jsonField) return;
      if (!scope._isDetailsForm) {
        handle(context);
      }
    });

    scope.$watch("isReadonly()", watcher);
    scope.$watch("isRequired()", watcher);
    scope.$watch("isValid()", watcher);

    var expr = $parse(condition);

    function watcher(current, old) {
      var rec = scope.record;
      if (rec === undefined && current === old) return;
      if (rec === undefined && scope.getContext) {
        rec = scope.getContext();
      }
      handle(rec);
    }

    function handle(rec) {
      var value;
      try {
        value = !!axelor.$eval(scope, expr, withContext(scope, rec));
      } catch (e) {
        console.error('FAILED:', condition, e);
      }
      // defer attr change to allow field init, see RM-14998
      scope.$applyAsync(function () {
        scope.attr(attr, negative ? !value : value);
      });
    }
  }

  function handleHilites(scope, field) {
    if (!field || _.isEmpty(field.hilites)) {
      return;
    }

    var hilites = field.hilites || [];
    var exprs = _.map(_.pluck(hilites, 'condition'), $parse);

    function handle(rec) {
      for (var i = 0; i < hilites.length; i++) {
        var hilite = hilites[i];
        var expr = exprs[i];
        var value = false;
        try {
          value = axelor.$eval(scope, expr, withContext(scope, rec));
        } catch (e) {
          console.error('FAILED:', hilite, e);
        }
        if (value) {
          return scope.attr('highlight', {
            hilite: hilite,
            passed: value
          });
        }
      }
      return scope.attr('highlight', {});
    }

    scope.$on("on:record-change", function(e, rec) {
      if (rec === scope.record) {
        handle(rec);
      }
    });
  }

  function handleBind(scope, field) {

    if (!field.bind || !field.name) {
      return;
    }

    var expr = $interpolate(field.bind);
    var last = null;

    function handle(rec) {
      var value;
      try {
        value = expr(withContext(scope, rec));
        if (value.length === 0) {
          value = null;
        }
      } catch (e) {
        console.error('FAILED:', field.bind, e);
      }

      if (scope.setValue && scope.record && last !== value) {
        scope.setValue(last = value);
      }
    }

    scope.$on("on:record-change", function(e, rec) {
      if (field && field.jsonField) {
        handle(scope.record);
      } else if (rec && rec === scope.record) {
        handle(rec);
      }
    });
  }

  function handleValueExpr(scope, field) {

    if (!field.valueExpr || !field.name) {
      return;
    }

    var expr = $parse(field.valueExpr);

    function handle(rec) {
      var value;
      try {
        value = axelor.$eval(scope, expr, withContext(scope, rec));
        if (value && value.length === 0) {
          value = null;
        }
      } catch (e) {
        console.error('FAILED:', field.valueExpr, e);
      }

      if (scope.setValue && scope.record) {
        scope.setValue(value, false);
      }
    }

    scope.$on("on:record-change", function(e, rec) {
      scope.$timeout(function () {
        if (field && field.jsonField) {
          handle(scope.record);
        } else if (rec && rec === scope.record) {
          handle(rec);
        }
      });
    });
  }

  function handleFor(scope, field, attr, conditional, negative) {
    if (field[conditional]) {
      handleCondition(scope, field, attr, field[conditional], negative);
    }
  }

  function handleForField(scope) {
    var field = scope.field;
    if (!field) return;
    handleFor(scope, field, "valid", "validIf");
    handleFor(scope, field, "hidden", "hideIf");
    handleFor(scope, field, "hidden", "showIf", true);
    handleFor(scope, field, "readonly", "readonlyIf");
    handleFor(scope, field, "required", "requiredIf");
    handleFor(scope, field, "collapse", "collapseIf");
    handleFor(scope, field, "canNew", "canNew");
    handleFor(scope, field, "canView", "canView");
    handleFor(scope, field, "canEdit", "canEdit");
    handleFor(scope, field, "canRemove", "canRemove");
    handleFor(scope, field, "canSelect", "canSelect");
    handleHilites(scope, field);
    handleBind(scope, field);
    handleValueExpr(scope, field);
  }

  function handleForView(scope) {
    var field = scope.schema;
    if (!field) return;
    handleFor(scope, field, "canNew", "canNew");
    handleFor(scope, field, "canEdit", "canEdit");
    handleFor(scope, field, "canSave", "canSave");
    handleFor(scope, field, "canCopy", "canCopy");
    handleFor(scope, field, "canDelete", "canDelete");
    handleFor(scope, field, "canArchive", "canArchive");
    handleFor(scope, field, "canAttach", "canAttach");
  }

  return function(scope, element, attrs) {
    scope.$evalAsync(function() {
      if (element.is('[ui-form]')) {
        return handleForView(scope);
      }
      handleForField(scope);
    });
  };
}]);

})();
<commit_msg>Fix hilite expression parsing issue<commit_after>/*
 * Axelor Business Solutions
 *
 * Copyright (C) 2005-2019 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');

/**
 * The Form widget.
 *
 */
ui.formWidget('Form', {

  priority: 100,

  css: "dynamic-form",

  scope: false,

  compile: function(element, attrs) {

    element.hide();
    element.find('[x-field],[data-field]').each(function(){

      var elem = $(this),
        name = elem.attr('x-field') || elem.attr('data-field');

      if (name && elem.attr('ui-button') === undefined) {
        if (!elem.attr('ng-model')) {
          elem.attr('ng-model', 'record.' + name);
        }
        if (!elem.attr('ng-required')) {
          // always attache a required validator to make
          // dynamic `required` attribute change effective
          elem.attr('ng-required', false);
        }
      }
    });

    return ui.formCompile.apply(this, arguments);
  },

  link: function(scope, element, attrs, controller) {

    element.on('submit', function(e) {
      e.preventDefault();
    });

    scope.$watch('record', function formRecordWatch(rec, old) {
      if (element.is(':visible')) {
        return;
      }
      scope.ajaxStop(function() {
        element.show();
        axelor.$adjustSize();
      });
    });
  }
});

/**
 * This directive is used filter $watch on scopes of inactive tabs.
 *
 */
ui.directive('uiTabGate', function() {

  return {

    compile: function compile(tElement, tAttrs) {

      return {
        pre: function preLink(scope, element, attrs) {
          scope.$watchChecker(function(current) {
            if (current.tabSelected === undefined) {
              return !scope.tab || scope.tab.selected === undefined || scope.tab.selected;
            }
            return current.tabSelected;
          });
        }
      };
    }
  };
});

/**
 * This directive is used to filter $watch on scopes of hidden forms.
 *
 */
ui.directive('uiFormGate', function() {

  return {
    compile: function compile(tElement, tAttrs) {

      return {
        pre: function preLink(scope, element, attrs) {
          var parent = null;
          scope.$watchChecker(function(current) {
            if (scope.tabSelected === false) {
              return false;
            }
            if (parent === null) {
              parent = element.parents('[ui-show]:first');
            }
            // hack for hidden nested editors (#2173)
            if (scope.$$forceWatch) {
              return true;
            }
            return !(parent.hasClass('ui-hide') || parent.hasClass('ui-hide'));
          });
        }
      };
    }
  };
});

/**
 * This directive is used to filter $watch on scopes based on some condition.
 *
 */
ui.directive('uiWatchIf', ['$parse', function($parse) {

  return {
    compile: function compile(tElement, tAttrs) {
      return {
        pre: function preLink(scope, element, attrs) {
          var value = false,
            expression = $parse(attrs.uiWatchIf);

          scope.$watchChecker(function (current) {
            if (current === scope) {
              return value = expression(scope);
            }
            return value;
          });
        }
      };
    }
  };
}]);

function toBoolean(value) {
  if (value && value.length !== 0) {
    var v = angular.lowercase("" + value);
    value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]');
  } else {
    value = false;
  }
  return value;
}

/**
 * This directive is used to speedup uiFormGate.
 */
ui.directive('uiShow', function() {

  return {
    scope: true, // create new scope to always watch the expression
    link: function link(scope, element, attrs) {
      scope.$$shouldWatch = true;
      scope.$watch(attrs.uiShow, function uiShowWatchAction(value){
        var val = toBoolean(value);
        element.css({ display: val ? '' : 'none', opacity: 0 }).toggleClass('ui-hide', !val);
        if (val) {
          element.animate({ opacity: 1 }, 300);
        }
      });
    }
  };
});

/**
 * This directive is used by view-pane to attach/detach element from DOM tree
 */
ui.directive('uiAttach', function () {
  return function (scope, element, attrs) {
    var parent = null;
    var uiAttachWatch = function uiAttachWatch(attach) {
      var result = toBoolean(attach);
      if (result) {
        if (parent) {
          element.appendTo(parent);
          parent = null;
          scope.$broadcast('dom:attach');
        }
      } else {
        parent = element.parent();
        scope.$broadcast('dom:detach');
        setTimeout(function () {
          element.detach();
        }, 100);
      }
    };

    uiAttachWatch.uiAttachWatch = true;

    scope.$watch(attrs.uiAttach, uiAttachWatch, true);
    scope.$on('$destroy', function () {
      if (parent) {
        parent = null;
        element.remove();
      }
    });
  };
});

/**
 * This directive can be used by widget to restore scroll when element is re-attached to DOM tree.
 */
ui.directive('uiAttachScroll', function () {
  return function (scope, element, attrs) {
    setTimeout(function () {
      var elem = element;
      var scrollTop = 0;

      if (attrs.uiAttachScroll) {
        elem = element.find(attrs.uiAttachScroll);
      }

      elem.on('scroll', function () {
        scrollTop = this.scrollTop;
      });

      function resetScroll() {
        elem.scrollTop(scrollTop);
      }

      scope.$on('dom:attach', resetScroll);
      scope.$on('tab:select', resetScroll);
    }, 300);
  };
});

ui.directive('uiWidgetStates', ['$parse', '$interpolate', function($parse, $interpolate) {

  function isValid(scope, name) {
    if (!name) return scope.isValid();
    var ctrl = scope.form;
    if (ctrl) {
      ctrl = ctrl[name];
    }
    if (ctrl) {
      return ctrl.$valid;
    }
  }

  function withContext(scope, record) {
    var values = _.extend({}, scope._context, scope._jsonContext, record);
    return _.extend(values, {
      $user: axelor.config['user.login'],
      $group: axelor.config['user.group'],
      $userId: axelor.config['user.id'],
    });
  }

  function handleCondition(scope, field, attr, condition, negative) {

    if (!condition || _.isBoolean(condition)) {
      return;
    }

    scope.$on("on:record-change", function(e, rec, force) {
      if (field && field.jsonField) {
        handle(scope.record);
      } else if (rec === scope.record || force) {
        handle(rec);
      }
    });
    scope.$on("on:grid-selection-change", function(e, context) {
      if (field && field.jsonField) return;
      if (!scope._isDetailsForm) {
        handle(context);
      }
    });

    scope.$watch("isReadonly()", watcher);
    scope.$watch("isRequired()", watcher);
    scope.$watch("isValid()", watcher);

    var expr = $parse(condition);

    function watcher(current, old) {
      var rec = scope.record;
      if (rec === undefined && current === old) return;
      if (rec === undefined && scope.getContext) {
        rec = scope.getContext();
      }
      handle(rec);
    }

    function handle(rec) {
      var value;
      try {
        value = !!axelor.$eval(scope, expr, withContext(scope, rec));
      } catch (e) {
        console.error('FAILED:', condition, e);
      }
      // defer attr change to allow field init, see RM-14998
      scope.$applyAsync(function () {
        scope.attr(attr, negative ? !value : value);
      });
    }
  }

  function handleHilites(scope, field) {
    if (!field || _.isEmpty(field.hilites)) {
      return;
    }

    var hilites = field.hilites || [];
    var exprs = _.map(_.pluck(hilites, 'condition'), function (s) { return $parse(s); });

    function handle(rec) {
      for (var i = 0; i < hilites.length; i++) {
        var hilite = hilites[i];
        var expr = exprs[i];
        var value = false;
        try {
          value = axelor.$eval(scope, expr, withContext(scope, rec));
        } catch (e) {
          console.error('FAILED:', hilite, e);
        }
        if (value) {
          return scope.attr('highlight', {
            hilite: hilite,
            passed: value
          });
        }
      }
      return scope.attr('highlight', {});
    }

    scope.$on("on:record-change", function(e, rec) {
      if (rec === scope.record) {
        handle(rec);
      }
    });
  }

  function handleBind(scope, field) {

    if (!field.bind || !field.name) {
      return;
    }

    var expr = $interpolate(field.bind);
    var last = null;

    function handle(rec) {
      var value;
      try {
        value = expr(withContext(scope, rec));
        if (value.length === 0) {
          value = null;
        }
      } catch (e) {
        console.error('FAILED:', field.bind, e);
      }

      if (scope.setValue && scope.record && last !== value) {
        scope.setValue(last = value);
      }
    }

    scope.$on("on:record-change", function(e, rec) {
      if (field && field.jsonField) {
        handle(scope.record);
      } else if (rec && rec === scope.record) {
        handle(rec);
      }
    });
  }

  function handleValueExpr(scope, field) {

    if (!field.valueExpr || !field.name) {
      return;
    }

    var expr = $parse(field.valueExpr);

    function handle(rec) {
      var value;
      try {
        value = axelor.$eval(scope, expr, withContext(scope, rec));
        if (value && value.length === 0) {
          value = null;
        }
      } catch (e) {
        console.error('FAILED:', field.valueExpr, e);
      }

      if (scope.setValue && scope.record) {
        scope.setValue(value, false);
      }
    }

    scope.$on("on:record-change", function(e, rec) {
      scope.$timeout(function () {
        if (field && field.jsonField) {
          handle(scope.record);
        } else if (rec && rec === scope.record) {
          handle(rec);
        }
      });
    });
  }

  function handleFor(scope, field, attr, conditional, negative) {
    if (field[conditional]) {
      handleCondition(scope, field, attr, field[conditional], negative);
    }
  }

  function handleForField(scope) {
    var field = scope.field;
    if (!field) return;
    handleFor(scope, field, "valid", "validIf");
    handleFor(scope, field, "hidden", "hideIf");
    handleFor(scope, field, "hidden", "showIf", true);
    handleFor(scope, field, "readonly", "readonlyIf");
    handleFor(scope, field, "required", "requiredIf");
    handleFor(scope, field, "collapse", "collapseIf");
    handleFor(scope, field, "canNew", "canNew");
    handleFor(scope, field, "canView", "canView");
    handleFor(scope, field, "canEdit", "canEdit");
    handleFor(scope, field, "canRemove", "canRemove");
    handleFor(scope, field, "canSelect", "canSelect");
    handleHilites(scope, field);
    handleBind(scope, field);
    handleValueExpr(scope, field);
  }

  function handleForView(scope) {
    var field = scope.schema;
    if (!field) return;
    handleFor(scope, field, "canNew", "canNew");
    handleFor(scope, field, "canEdit", "canEdit");
    handleFor(scope, field, "canSave", "canSave");
    handleFor(scope, field, "canCopy", "canCopy");
    handleFor(scope, field, "canDelete", "canDelete");
    handleFor(scope, field, "canArchive", "canArchive");
    handleFor(scope, field, "canAttach", "canAttach");
  }

  return function(scope, element, attrs) {
    scope.$evalAsync(function() {
      if (element.is('[ui-form]')) {
        return handleForView(scope);
      }
      handleForField(scope);
    });
  };
}]);

})();
<|endoftext|>


<commit_before>function leaflet_alert() {
    alert("Clicking OK the user takes full responsibility of using this map.");
}

function leaflet_alert2() {
  var txt;
  var r = confirm("Clicking OK the user takes full responsibility of using this map.");
  if (r != true) {
      txt = "You pressed Cancel";
  }
}
<commit_msg>Update custom_funct.js<commit_after>function leaflet_alert() {
    alert("Clicking OK the user takes full responsibility of using this map.");
}

function leaflet_alert2() {
  var txt;
  var r = confirm("By clicking OK the user takes full responsibility of using this map and absolves the map developper of any responsibility.");
  if (r != true) {
      window.open('https://edrap.github.io/abruzzomap', '_blank');
  }
}
<|endoftext|>


<commit_before>
var _ = require('lodash')
var AccessControlProcedure = require('access-controls')

var DEBUG = false

var ACL_ERROR_CODE = 'perm/fail/acl'

function debug() {
  if(DEBUG) {
    console.log.apply(console, arguments)
  }
}

function ACLMicroservicesBuilder(seneca) {
  this._ACLProcedureResolver = undefined
  this._seneca = seneca
  this._allowedProperties = [];

  var self = this

//   seneca.add({role: 'entity', cmd: 'save'}, function(args, callback) {
//     console.log(JSON.stringify(args))
//     this.prior(args, callback)
//   })

  this._executeReadPermissionsWrapper = function(args, callback) {

    if(args.perm$) {

      self._executeReadPermissions(args, callback)

    } else {

      this.prior(args, callback)

    }
  }

  this._executeSavePermissionsWrapper = function(args, callback) {

    if(args.perm$) {

      debug(JSON.stringify(args))
      self._executeSavePermissions(args, callback)

    } else {

      this.prior(args, callback)

    }
  }

  this._executeListPermissionsWrapper = function(args, callback) {

    if(args.perm$) {

      debug(JSON.stringify(args))
      self._executeListPermissions(args, callback)

    } else {

      this.prior(args, callback)

    }
  }

  this._executeRemovePermissionsWrapper = function(args, callback) {
    if(args.perm$) {

      debug(JSON.stringify(args))
      self._executeRemovePermissions(args, callback)

    } else {

      this.prior(args, callback)

    }
  }
}

ACLMicroservicesBuilder.prototype.register = function(accessControls, properties) {
  this._ACLProcedureResolver = AccessControlProcedure.generateActionsMapping(accessControls)
  this._allowedProperties = properties;
}

ACLMicroservicesBuilder.prototype.augmentSeneca = function(seneca) {

  var filterList = this._ACLProcedureResolver.list()
  for(var i = 0 ; i < filterList.length ; i++) {

    switch(filterList[i].match.cmd) {
      case 'load':
        seneca.add(filterList[i].match, this._executeReadPermissionsWrapper)
        break
      case 'save':
        seneca.add(filterList[i].match, this._executeSavePermissionsWrapper)
        break
      case 'list':
        seneca.add(filterList[i].match, this._executeListPermissionsWrapper)
        break
      case 'remove':
        seneca.add(filterList[i].match, this._executeRemovePermissionsWrapper)
        break
      default:
        console.warn('Permissions: command [' + filterList[i].match.cmd + '] is not implemented but is used in an ACL rule.')
    }

  }
}

ACLMicroservicesBuilder.prototype._executeRemovePermissions = function(args, callback) {
  var self = this

  var roles = extractRoles(args)
  var context = extractContext(args)
  var entityDef = extractEntityDef(args)

  delete args.perm$

  if(args.q.id) {

    this._loadAndAuthorize(entityDef, args.q.id, args.cmd, roles, context, function(err, dbEntity) {

      if(err) {

        callback(err, undefined)

      } else {

        self._seneca.act(args, function(err, entity) {
          if(err) {
            return callback(err, undefined)
          }
          // the entity returned by the remove needs to be filtered again
          self._filter(entityDef, entity, 'load', roles, context, function(err, entity) {
            callback(err, entity)
          })
        })

      }
    })

  } else {
    var err = new Error('ACL permissions deny multi delete. A query id is required')
    callback(err, undefined)
  }

}

ACLMicroservicesBuilder.prototype._executeReadPermissions = function(args, callback) {
  var self = this
  var roles = extractRoles(args)
  var context = extractContext(args)
  var entityDef = extractEntityDef(args)

  delete args.perm$

  this._seneca.act(args, function(err, entity) {

    if(err || !entity) {
      callback(err, undefined)
    } else {
      // console.log('ENTITY', entity)
      // console.log('CONTEXT', context)
      self._deepAuthorize(entityDef, entity, args.cmd, args.cmd, roles, context, true, function(err, entity) {
        callback(err, entity)
      })

    }

  })
}

ACLMicroservicesBuilder.prototype._executeListPermissions = function(args, callback) {
  var self = this
  var roles = extractRoles(args)
  var context = extractContext(args)
  var entityDef = extractEntityDef(args)

  delete args.perm$

  this._seneca.act(args, function(err, entities) {

    if(err) {
      callback(err, undefined)
    } else if(entities && entities.length > 0) {

      var filteredEntities = []
      var callbackCount = 0
      var stopAll = false

      // this closure is evil
      function processAuthResultForEntity(err, entity) {

        if(stopAll) return

        if(err && err.code !== ACL_ERROR_CODE) {
          stopAll = true
          callback(err, undefined)
        } else {

          if(entity) {
            filteredEntities.push(entity)
          }
          callbackCount ++
          if(callbackCount === entities.length) {
            callback(undefined, filteredEntities)
          }
        }
      }

      for(var i = 0 ; i < entities.length ; i++) {
        self._deepAuthorize(entityDef, entities[i], args.cmd, args.cmd, roles, context, true, processAuthResultForEntity)
      }

    } else {
      callback(undefined, [])
    }

  })
}

ACLMicroservicesBuilder.prototype._executeSavePermissions = function(args, callback) {
  var self = this

  var roles = extractRoles(args)
  var context = extractContext(args)
  var entityDef = extractEntityDef(args)

  delete args.perm$

  if(args.ent.id) { // update

    this._loadAndAuthorize(entityDef, args.ent.id, args.cmd, roles, context, function(err, dbEntity) {

      if(err) {

        callback(err, undefined)

      } else {

        // also execute permission checks on the new attributes
        self._deepAuthorize(entityDef, args.ent, args.cmd, 'save_new', roles, context, true, function(err, filteredEntity) {

          if(err) {

            callback(err, undefined)

          } else {

            merge(dbEntity, args.ent)

            delete args.perm$

            self._seneca.act(args, function(err, entity) {
              if(err) {
                return callback(err, undefined)
              }
              // the entity returned by the save needs to be filtered again
              self._filter(entityDef, entity, 'load', roles, context, function(err, entity) {
                callback(err, entity)
              })

            })
          }

        })
      }
    })

  } else { // create

    self._deepAuthorize(entityDef, args.ent, args.cmd, 'save_new', roles, context, true, function(err, filteredEntity) {

      if(err) {
        callback(err, undefined)
      } else {

        delete args.perm$

        self._seneca.act(args, function(err, entity) {
          if(err) {
            return callback(err, undefined)
          }
          // the entity returned by the save needs to be filtered again
          self._filter(entityDef, entity, 'load', roles, context, function(err, entity) {
            callback(err, entity)
          })
        })
      }

    })
  }

}

ACLMicroservicesBuilder.prototype._loadAndAuthorize = function(entityDef, entityId, action, roles, context, callback) {

  var self = this

  var ent = this._seneca.make(canonize(entityDef))

  ent.load$(entityId, function(err, dbEntity) {
    if(err || !dbEntity) {
      return callback(err, null)
    }

    if(action === 'save') {
      var ruleAction = 'save_existing'
    } else {
      ruleAction = action
    }

    self._deepAuthorize(entityDef, dbEntity, action, ruleAction, roles, context, false, function(err, entity) {
      callback(err, err ? undefined : dbEntity)
    })
  })
}

ACLMicroservicesBuilder.prototype._filter = function(entityDef, entity, action, roles, context, callback) {
  var aclProcedure = AccessControlProcedure.getProcedureForEntity(this._ACLProcedureResolver, entityDef, action)
  if(aclProcedure) {
    aclProcedure.authorize(entity, action, roles, context, function(err, authDecision) {
      if(err) {
        callback(err, undefined)
      } else {
        aclProcedure.applyFilters(authDecision.filters, entity, action)
        callback(undefined, entity)
      }
    })
  } else {
    setImmediate(function() {
      callback(undefined, entity)
    })
  }
}

ACLMicroservicesBuilder.prototype._deepAuthorize = function(entityDef, entity, action, ruleAction, roles, context, applyFilters, callback) {
  var self = this

  var aclProcedure = AccessControlProcedure.getProcedureForEntity(self._ACLProcedureResolver, entityDef, action)

  var allowedProperties = this._allowedProperties

  debug('_deepAuthorize', action, entityDef, 'id=', entity.id, 'roles=', roles, aclProcedure ? 'with procedure' : 'no procedure')
  if(aclProcedure) {
    aclProcedure.authorize(entity, ruleAction, roles, context, function(err, authDecision) {
      if(err) {
        callback(err, undefined)
      } else {
        //console.log(JSON.stringify(authDecision, null, 2))
        debug('_deepAuthorize decision', JSON.stringify(authDecision))
        var inheritDetails = inherit(authDecision)

        if(applyFilters) {
          aclProcedure.applyFilters(authDecision.filters, entity, action)
        }

        if(inheritDetails) {

          // TODO: log
          self._loadAndAuthorize(inheritDetails.entity, inheritDetails.id, action, roles, context, function(err, inheritedEntity) {

            if(err) {
              callback(err, undefined)
            } else {
              callback(undefined, entity)
            }

          })

        } else if(authDecision.authorize) {

          //TODO: log auth granted
          callback(undefined, entity)

        } else if(!authDecision.authorize && !authDecision.hard && action === 'list') {
          for(var property in entity) {
            var propertyAllowed = allowedProperties.indexOf(property);
            if(propertyAllowed === -1) {
              if(entity.hasOwnProperty(property)) {
                delete entity[property];
              }
            }
          }
          console.log("**** case entity = " + JSON.stringify(entity));
          callback(undefined, entity)

        } else if(!authDecision.authorize && authDecision.hard && action === 'list') {
          callback(error(self._seneca, authDecision), undefined)
        } else {

          // TODO: log
          callback(error(self._seneca, authDecision), undefined)
        }

      }
    })
  } else {
    console.log('no acls for', JSON.stringify(entityDef), action)
    callback(undefined, entity)
  }
}

function error(seneca, authDecision) {
  var err = new Error('Permission Denied')
  err.reason = authDecision
  err.code = ACL_ERROR_CODE
  err.status = 401
  err.httpstatus = 401
  err.seneca = seneca
  err.toString = selfErrorString
  return err
}

function selfErrorString() {
  var jsonReadyError = {
    message: this.message,
    status: this.status,
    code: this.code,
    reason: this.reason
  };
  return JSON.stringify(jsonReadyError);
}

function extractRoles(args) {
  return args.perm$.roles
}

function extractContext(args) {
  return {
    user: args.user$
  }
}

function extractEntityDef(args) {
  return {
    zone: args.zone,
    base: args.base,
    name: args.name
  }
}

function canonize(entityDef) {
  return (entityDef.zone || '-') + '/' + (entityDef.base || '-') + '/' + entityDef.name
}

function inherit(authDecision) {
  if(authDecision.authorize && authDecision.inherit && authDecision.inherit.length > 0) {
    return authDecision.inherit[0]
  }
  return false
}

/** Unfortunately, this is mostly for the unit tests to pass.
 * The in-memory store behaviour will completely replace an existing object with
 *   the one being saved. Hence we need to do an in-memory merge before saving.
 * Bug opened against seneca: https://github.com/rjrodger/seneca/issues/43
 */
function merge(source, destination) {
  for(var attr in source) {
    if(!destination.hasOwnProperty(attr) && !/\$$/.test(attr)) {
      destination[attr] = source[attr]
    }
  }
}

ACLMicroservicesBuilder.ACL_ERROR_CODE = ACL_ERROR_CODE

module.exports = ACLMicroservicesBuilder
<commit_msg>update files<commit_after>
var _ = require('lodash')
var AccessControlProcedure = require('access-controls')

var DEBUG = false

var ACL_ERROR_CODE = 'perm/fail/acl'

function debug() {
  if(DEBUG) {
    console.log.apply(console, arguments)
  }
}

function ACLMicroservicesBuilder(seneca) {
  this._ACLProcedureResolver = undefined
  this._seneca = seneca
  this._allowedProperties = [];

  var self = this

//   seneca.add({role: 'entity', cmd: 'save'}, function(args, callback) {
//     console.log(JSON.stringify(args))
//     this.prior(args, callback)
//   })

  this._executeReadPermissionsWrapper = function(args, callback) {

    if(args.perm$) {

      self._executeReadPermissions(args, callback)

    } else {

      this.prior(args, callback)

    }
  }

  this._executeSavePermissionsWrapper = function(args, callback) {

    if(args.perm$) {

      debug(JSON.stringify(args))
      self._executeSavePermissions(args, callback)

    } else {

      this.prior(args, callback)

    }
  }

  this._executeListPermissionsWrapper = function(args, callback) {

    if(args.perm$) {

      debug(JSON.stringify(args))
      self._executeListPermissions(args, callback)

    } else {

      this.prior(args, callback)

    }
  }

  this._executeRemovePermissionsWrapper = function(args, callback) {
    if(args.perm$) {

      debug(JSON.stringify(args))
      self._executeRemovePermissions(args, callback)

    } else {

      this.prior(args, callback)

    }
  }
}

ACLMicroservicesBuilder.prototype.register = function(accessControls, properties) {
  this._ACLProcedureResolver = AccessControlProcedure.generateActionsMapping(accessControls)
  this._allowedProperties = properties;
}

ACLMicroservicesBuilder.prototype.augmentSeneca = function(seneca) {

  var filterList = this._ACLProcedureResolver.list()
  for(var i = 0 ; i < filterList.length ; i++) {

    switch(filterList[i].match.cmd) {
      case 'load':
        seneca.add(filterList[i].match, this._executeReadPermissionsWrapper)
        break
      case 'save':
        seneca.add(filterList[i].match, this._executeSavePermissionsWrapper)
        break
      case 'list':
        seneca.add(filterList[i].match, this._executeListPermissionsWrapper)
        break
      case 'remove':
        seneca.add(filterList[i].match, this._executeRemovePermissionsWrapper)
        break
      default:
        console.warn('Permissions: command [' + filterList[i].match.cmd + '] is not implemented but is used in an ACL rule.')
    }

  }
}

ACLMicroservicesBuilder.prototype._executeRemovePermissions = function(args, callback) {
  var self = this

  var roles = extractRoles(args)
  var context = extractContext(args)
  var entityDef = extractEntityDef(args)

  delete args.perm$

  if(args.q.id) {

    this._loadAndAuthorize(entityDef, args.q.id, args.cmd, roles, context, function(err, dbEntity) {

      if(err) {

        callback(err, undefined)

      } else {

        self._seneca.act(args, function(err, entity) {
          if(err) {
            return callback(err, undefined)
          }
          // the entity returned by the remove needs to be filtered again
          self._filter(entityDef, entity, 'load', roles, context, function(err, entity) {
            callback(err, entity)
          })
        })

      }
    })

  } else {
    var err = new Error('ACL permissions deny multi delete. A query id is required')
    callback(err, undefined)
  }

}

ACLMicroservicesBuilder.prototype._executeReadPermissions = function(args, callback) {
  var self = this
  var roles = extractRoles(args)
  var context = extractContext(args)
  var entityDef = extractEntityDef(args)

  delete args.perm$

  this._seneca.act(args, function(err, entity) {

    if(err || !entity) {
      callback(err, undefined)
    } else {
      // console.log('ENTITY', entity)
      // console.log('CONTEXT', context)
      self._deepAuthorize(entityDef, entity, args.cmd, args.cmd, roles, context, true, function(err, entity) {
        callback(err, entity)
      })

    }

  })
}

ACLMicroservicesBuilder.prototype._executeListPermissions = function(args, callback) {
  var self = this
  var roles = extractRoles(args)
  var context = extractContext(args)
  var entityDef = extractEntityDef(args)

  delete args.perm$

  this._seneca.act(args, function(err, entities) {

    if(err) {
      callback(err, undefined)
    } else if(entities && entities.length > 0) {

      var filteredEntities = []
      var callbackCount = 0
      var stopAll = false

      // this closure is evil
      function processAuthResultForEntity(err, entity) {

        if(stopAll) return

        if(err && err.code !== ACL_ERROR_CODE) {
          stopAll = true
          callback(err, undefined)
        } else {

          if(entity) {
            filteredEntities.push(entity)
          }
          callbackCount ++
          if(callbackCount === entities.length) {
            callback(undefined, filteredEntities)
          }
        }
      }

      for(var i = 0 ; i < entities.length ; i++) {
        self._deepAuthorize(entityDef, entities[i], args.cmd, args.cmd, roles, context, true, processAuthResultForEntity)
      }

    } else {
      callback(undefined, [])
    }

  })
}

ACLMicroservicesBuilder.prototype._executeSavePermissions = function(args, callback) {
  var self = this

  var roles = extractRoles(args)
  var context = extractContext(args)
  var entityDef = extractEntityDef(args)

  delete args.perm$

  if(args.ent.id) { // update

    this._loadAndAuthorize(entityDef, args.ent.id, args.cmd, roles, context, function(err, dbEntity) {

      if(err) {

        callback(err, undefined)

      } else {

        // also execute permission checks on the new attributes
        self._deepAuthorize(entityDef, args.ent, args.cmd, 'save_new', roles, context, true, function(err, filteredEntity) {

          if(err) {

            callback(err, undefined)

          } else {

            merge(dbEntity, args.ent)

            delete args.perm$

            self._seneca.act(args, function(err, entity) {
              if(err) {
                return callback(err, undefined)
              }
              // the entity returned by the save needs to be filtered again
              self._filter(entityDef, entity, 'load', roles, context, function(err, entity) {
                callback(err, entity)
              })

            })
          }

        })
      }
    })

  } else { // create

    self._deepAuthorize(entityDef, args.ent, args.cmd, 'save_new', roles, context, true, function(err, filteredEntity) {

      if(err) {
        callback(err, undefined)
      } else {

        delete args.perm$

        self._seneca.act(args, function(err, entity) {
          if(err) {
            return callback(err, undefined)
          }
          // the entity returned by the save needs to be filtered again
          self._filter(entityDef, entity, 'load', roles, context, function(err, entity) {
            callback(err, entity)
          })
        })
      }

    })
  }

}

ACLMicroservicesBuilder.prototype._loadAndAuthorize = function(entityDef, entityId, action, roles, context, callback) {

  var self = this

  var ent = this._seneca.make(canonize(entityDef))

  ent.load$(entityId, function(err, dbEntity) {
    if(err || !dbEntity) {
      return callback(err, null)
    }

    if(action === 'save') {
      var ruleAction = 'save_existing'
    } else {
      ruleAction = action
    }

    self._deepAuthorize(entityDef, dbEntity, action, ruleAction, roles, context, false, function(err, entity) {
      callback(err, err ? undefined : dbEntity)
    })
  })
}

ACLMicroservicesBuilder.prototype._filter = function(entityDef, entity, action, roles, context, callback) {
  var aclProcedure = AccessControlProcedure.getProcedureForEntity(this._ACLProcedureResolver, entityDef, action)
  if(aclProcedure) {
    aclProcedure.authorize(entity, action, roles, context, function(err, authDecision) {
      if(err) {
        callback(err, undefined)
      } else {
        aclProcedure.applyFilters(authDecision.filters, entity, action)
        callback(undefined, entity)
      }
    })
  } else {
    setImmediate(function() {
      callback(undefined, entity)
    })
  }
}

ACLMicroservicesBuilder.prototype._deepAuthorize = function(entityDef, entity, action, ruleAction, roles, context, applyFilters, callback) {
  var self = this

  var aclProcedure = AccessControlProcedure.getProcedureForEntity(self._ACLProcedureResolver, entityDef, action)

  var allowedProperties = this._allowedProperties

  debug('_deepAuthorize', action, entityDef, 'id=', entity.id, 'roles=', roles, aclProcedure ? 'with procedure' : 'no procedure')
  if(aclProcedure) {
    aclProcedure.authorize(entity, ruleAction, roles, context, function(err, authDecision) {
      if(err) {
        callback(err, undefined)
      } else {
        //console.log(JSON.stringify(authDecision, null, 2))
        debug('_deepAuthorize decision', JSON.stringify(authDecision))
        var inheritDetails = inherit(authDecision)

        if(applyFilters) {
          aclProcedure.applyFilters(authDecision.filters, entity, action)
        }

        if(inheritDetails) {

          // TODO: log
          self._loadAndAuthorize(inheritDetails.entity, inheritDetails.id, action, roles, context, function(err, inheritedEntity) {

            if(err) {
              callback(err, undefined)
            } else {
              callback(undefined, entity)
            }

          })

        } else if(authDecision.authorize) {

          //TODO: log auth granted
          callback(undefined, entity)

        } else if(!authDecision.authorize && !authDecision.hard && action === 'list') {
          for(var property in entity) {
            var propertyAllowed = allowedProperties.indexOf(property);
            if(propertyAllowed === -1) {
              if(entity.hasOwnProperty(property)) {
                delete entity[property];
              }
            }
          }

          callback(undefined, entity)

        } else if(!authDecision.authorize && authDecision.hard && action === 'list') {
          callback(error(self._seneca, authDecision), undefined)
        } else {

          // TODO: log
          callback(error(self._seneca, authDecision), undefined)
        }

      }
    })
  } else {
    console.log('no acls for', JSON.stringify(entityDef), action)
    callback(undefined, entity)
  }
}

function error(seneca, authDecision) {
  var err = new Error('Permission Denied')
  err.reason = authDecision
  err.code = ACL_ERROR_CODE
  err.status = 401
  err.httpstatus = 401
  err.seneca = seneca
  err.toString = selfErrorString
  return err
}

function selfErrorString() {
  var jsonReadyError = {
    message: this.message,
    status: this.status,
    code: this.code,
    reason: this.reason
  };
  return JSON.stringify(jsonReadyError);
}

function extractRoles(args) {
  return args.perm$.roles
}

function extractContext(args) {
  return {
    user: args.user$
  }
}

function extractEntityDef(args) {
  return {
    zone: args.zone,
    base: args.base,
    name: args.name
  }
}

function canonize(entityDef) {
  return (entityDef.zone || '-') + '/' + (entityDef.base || '-') + '/' + entityDef.name
}

function inherit(authDecision) {
  if(authDecision.authorize && authDecision.inherit && authDecision.inherit.length > 0) {
    return authDecision.inherit[0]
  }
  return false
}

/** Unfortunately, this is mostly for the unit tests to pass.
 * The in-memory store behaviour will completely replace an existing object with
 *   the one being saved. Hence we need to do an in-memory merge before saving.
 * Bug opened against seneca: https://github.com/rjrodger/seneca/issues/43
 */
function merge(source, destination) {
  for(var attr in source) {
    if(!destination.hasOwnProperty(attr) && !/\$$/.test(attr)) {
      destination[attr] = source[attr]
    }
  }
}

ACLMicroservicesBuilder.ACL_ERROR_CODE = ACL_ERROR_CODE

module.exports = ACLMicroservicesBuilder
<|endoftext|>


<commit_before>// Load in dependencies
var fs = require('fs');
var path = require('path');
var _ = require('underscore');
var async = require('async');
var templater = require('spritesheet-templates');
var spritesmith = require('spritesmith');
var url = require('url2');

// Define class to contain different extension handlers
function ExtFormat() {
  this.formatObj = {};
}
ExtFormat.prototype = {
  add: function (name, val) {
    this.formatObj[name] = val;
  },
  get: function (filepath) {
    // Grab the extension from the filepath
    var ext = path.extname(filepath);
    var lowerExt = ext.toLowerCase();

    // Look up the file extenion from our format object
    var formatObj = this.formatObj;
    var format = formatObj[lowerExt];
    return format;
  }
};

// Create img and css formats
var imgFormats = new ExtFormat();
var cssFormats = new ExtFormat();

// Add our img formats
imgFormats.add('.png', 'png');
imgFormats.add('.jpg', 'jpeg');
imgFormats.add('.jpeg', 'jpeg');

// Add our css formats
cssFormats.add('.styl', 'stylus');
cssFormats.add('.stylus', 'stylus');
cssFormats.add('.sass', 'sass');
cssFormats.add('.scss', 'scss');
cssFormats.add('.less', 'less');
cssFormats.add('.json', 'json');
cssFormats.add('.css', 'css');

function getCoordinateName(filepath) {
  // Extract the image name (exlcuding extension)
  var fullname = path.basename(filepath);
  var nameParts = fullname.split('.');

  // If there is are more than 2 parts, pop the last one
  if (nameParts.length >= 2) {
    nameParts.pop();
  }

  // Return our modified filename
  return nameParts.join('.');
}

module.exports = function gruntSpritesmith (grunt) {
  // Create a SpriteMaker function
  function SpriteMaker() {
    // Grab the raw configuration
    var data = this.data;

    // If we were invoked via `grunt-newer`, re-localize the info
    if (data.src === undefined && data.files) {
      data = data.files[0] || {};
    }

    // Determine the origin and destinations
    var src = data.src;
    var destImg = data.dest;
    var destCss = data.destCss;
    var cssTemplate = data.cssTemplate;
    var that = this;

    // Verify all properties are here
    if (!src || !destImg || !destCss) {
      return grunt.fatal('grunt.sprite requires a src, dest (img), and destCss property');
    }

    // Expand all filepaths (e.g. `*.png` -> `home.png`)
    var srcFiles = grunt.file.expand(src);

    // If there are settings for retina
    var retinaSrcFiles;
    var retinaSrcFilter = data.retinaSrcFilter;
    var retinaDestImg = data.retinaDest;
    if (retinaSrcFilter || retinaDestImg) {
      grunt.log.debug('Retina settings detected');

      // Verify our required set is present
      if (!retinaSrcFilter || !retinaDestImg) {
        return grunt.fatal('Retina settings detected. We must have both `retinaSrcFilter` and `retinaDest` ' +
          'provided for retina to work');
      }

      // Filter out our retina files
      retinaSrcFiles = [];
      srcFiles = srcFiles.filter(function filterSrcFile (filepath) {
        // If we have a retina file, filter it out
        if (grunt.file.match(retinaSrcFilter, filepath).length) {
          retinaSrcFiles.push(filepath);
          return false;
        // Otherwise, keep it in the src files
        } else {
          return true;
        }
      });
      grunt.verbose.writeln('Retina images found: ' + retinaSrcFiles.join(', '));
    }

    // Create an async callback
    var cb = this.async();

    // Determine the format of the image
    var imgOpts = data.imgOpts || {};
    var imgFormat = imgOpts.format || imgFormats.get(destImg) || 'png';

    // Set up the defautls for imgOpts
    _.defaults(imgOpts, {format: imgFormat});

    // Perpare spritesmith parameters
    var spritesmithParams = {
      src: srcFiles,
      engine: data.engine,
      algorithm: data.algorithm,
      padding: data.padding || 0,
      algorithmOpts: data.algorithmOpts || {},
      engineOpts: data.engineOpts || {},
      exportOpts: imgOpts
    };

    // In parallel
    async.parallel([
      // Run our normal task
      function normalSpritesheet (callback) {
        spritesmith(spritesmithParams, callback);
      },
      // If we have a retina task, run it as well
      function retinaSpritesheet (callback) {
        // DEV: We don't check length since we could have no images passed in
        if (retinaSrcFiles) {
          var retinaParams = _.defaults({
            src: retinaSrcFiles
          }, spritesmithParams);
          spritesmith(retinaParams, callback);
        } else {
          process.nextTick(callback);
        }
      }
    ], function handleSpritesheets (err, resultArr) {
      // If an error occurred, callback with it
      if (err) {
        grunt.fatal(err);
        return cb(err);
      }

      // Otherwise, write out the result to destImg
      var result = resultArr[0];
      var destImgDir = path.dirname(destImg);
      grunt.file.mkdir(destImgDir);
      fs.writeFileSync(destImg, result.image, 'binary');

      // Generate a listing of CSS variables
      var coordinates = result.coordinates;
      var properties = result.properties;
      var spritePath = data.imgPath || url.relative(destCss, destImg);
      var spritesheetInfo = {
        width: properties.width,
        height: properties.height,
        image: spritePath
      };
      var cssVarMap = data.cssVarMap || function noop () {};
      var cleanCoords = [];

      // Clean up the file name of the file
      Object.getOwnPropertyNames(coordinates).sort().forEach(function prepareTemplateData (file) {
        // Extract out our name
        var name = getCoordinateName(file);
        var coords = coordinates[file];

        // Specify the image for the sprite
        coords.name = name;
        coords.source_image = file;
        // DEV: `image`, `total_width`, `total_height` are deprecated as they are overwritten in `spritesheet-templates`
        coords.image = spritePath;
        coords.total_width = properties.width;
        coords.total_height = properties.height;

        // Map the coordinates through cssVarMap
        coords = cssVarMap(coords) || coords;

        // Save the cleaned name and coordinates
        cleanCoords.push(coords);
      });

      // If we have retina sprites
      var retinaResult = resultArr[1];
      var retinaSpritesheetInfo;
      if (retinaResult) {
        // Write out the result to destImg
        var retinaDestImgDir = path.dirname(retinaDestImg);
        grunt.file.mkdir(retinaDestImgDir);
        fs.writeFileSync(retinaDestImg, retinaResult.image, 'binary');

        // Generate a listing of CSS variables
        var retinaCoordinates = retinaResult.coordinates;
        var retinaProperties = retinaResult.properties;
        var retinaSpritePath = data.retinaImgPath || url.relative(destCss, retinaDestImg);
        retinaSpritesheetInfo = {
          width: retinaProperties.width,
          height: retinaProperties.height,
          image: retinaSpritePath
        };
        // DEV: We reuse cssVarMap and cleanCoords

        // Clean up the file name of the file
        Object.getOwnPropertyNames(retinaCoordinates).sort().forEach(function prepareRetinaTemplateData (file) {
          var name = getCoordinateName(file);
          var coords = retinaCoordinates[file];
          coords.name = name;
          coords.source_image = file;
          coords.image = retinaSpritePath;
          coords.total_width = retinaProperties.width;
          coords.total_height = retinaProperties.height;
          coords = cssVarMap(coords) || coords;
          cleanCoords.push(coords);
        });
      }

      // If there is a custom template, use it
      var cssFormat = 'spritesmith-custom';
      var cssOptions = data.cssOpts || {};
      if (cssTemplate) {
        if (typeof cssTemplate === 'function') {
          templater.addTemplate(cssFormat, cssTemplate);
        } else {
          templater.addMustacheTemplate(cssFormat, fs.readFileSync(cssTemplate, 'utf8'));
        }
      } else {
      // Otherwise, override the cssFormat and fallback to 'json'
      // TODO: Concatenate on `-retina` to `destCss`
        cssFormat = data.cssFormat || cssFormats.get(destCss) || 'json';
      }

      // Render the variables via `spritesheet-templates`
      // TODO: Move to `hogan.js` and pick up inheritance
      //   https://github.com/twitter/hogan.js
      // TODO: Or maybe handlebars with handlebars-layouts
      //   https://github.com/shannonmoeller/handlebars-layouts
      var cssStr = templater({
        items: cleanCoords,
        spritesheet: spritesheetInfo,
        retinaSpritesheet: retinaSpritesheetInfo
      }, {
        format: cssFormat,
        formatOpts: cssOptions,
        spritesheetName: data.cssSpritesheetName,
        retinaSpritesheetName: data.retinaCssSpritesheetName
      });

      // Write it out to the CSS file
      var destCssDir = path.dirname(destCss);
      grunt.file.mkdir(destCssDir);
      fs.writeFileSync(destCss, cssStr, 'utf8');

      // Fail task if errors were logged.
      if (that.errorCount) { cb(false); }

      // Otherwise, print a success message.
      grunt.log.writeln('Files "' + destCss + '", "' + destImg + '" created.');

      // Callback
      cb(true);
    });
  }

  // Export the SpriteMaker function
  grunt.registerMultiTask('sprite', 'Spritesheet making utility', SpriteMaker);
};
<commit_msg>Hardcoding stylesheet for now<commit_after>// Load in dependencies
var fs = require('fs');
var path = require('path');
var _ = require('underscore');
var async = require('async');
var templater = require('spritesheet-templates');
var spritesmith = require('spritesmith');
var url = require('url2');

// Define class to contain different extension handlers
function ExtFormat() {
  this.formatObj = {};
}
ExtFormat.prototype = {
  add: function (name, val) {
    this.formatObj[name] = val;
  },
  get: function (filepath) {
    // Grab the extension from the filepath
    var ext = path.extname(filepath);
    var lowerExt = ext.toLowerCase();

    // Look up the file extenion from our format object
    var formatObj = this.formatObj;
    var format = formatObj[lowerExt];
    return format;
  }
};

// Create img and css formats
var imgFormats = new ExtFormat();
var cssFormats = new ExtFormat();

// Add our img formats
imgFormats.add('.png', 'png');
imgFormats.add('.jpg', 'jpeg');
imgFormats.add('.jpeg', 'jpeg');

// Add our css formats
cssFormats.add('.styl', 'stylus');
cssFormats.add('.stylus', 'stylus');
cssFormats.add('.sass', 'sass');
cssFormats.add('.scss', 'scss');
cssFormats.add('.less', 'less');
cssFormats.add('.json', 'json');
cssFormats.add('.css', 'css');

function getCoordinateName(filepath) {
  // Extract the image name (exlcuding extension)
  var fullname = path.basename(filepath);
  var nameParts = fullname.split('.');

  // If there is are more than 2 parts, pop the last one
  if (nameParts.length >= 2) {
    nameParts.pop();
  }

  // Return our modified filename
  return nameParts.join('.');
}

module.exports = function gruntSpritesmith (grunt) {
  // Create a SpriteMaker function
  function SpriteMaker() {
    // Grab the raw configuration
    var data = this.data;

    // If we were invoked via `grunt-newer`, re-localize the info
    if (data.src === undefined && data.files) {
      data = data.files[0] || {};
    }

    // Determine the origin and destinations
    var src = data.src;
    var destImg = data.dest;
    var destCss = data.destCss;
    var cssTemplate = data.cssTemplate;
    var that = this;

    // Verify all properties are here
    if (!src || !destImg || !destCss) {
      return grunt.fatal('grunt.sprite requires a src, dest (img), and destCss property');
    }

    // Expand all filepaths (e.g. `*.png` -> `home.png`)
    var srcFiles = grunt.file.expand(src);

    // If there are settings for retina
    var retinaSrcFiles;
    var retinaSrcFilter = data.retinaSrcFilter;
    var retinaDestImg = data.retinaDest;
    if (retinaSrcFilter || retinaDestImg) {
      grunt.log.debug('Retina settings detected');

      // Verify our required set is present
      if (!retinaSrcFilter || !retinaDestImg) {
        return grunt.fatal('Retina settings detected. We must have both `retinaSrcFilter` and `retinaDest` ' +
          'provided for retina to work');
      }

      // Filter out our retina files
      retinaSrcFiles = [];
      srcFiles = srcFiles.filter(function filterSrcFile (filepath) {
        // If we have a retina file, filter it out
        if (grunt.file.match(retinaSrcFilter, filepath).length) {
          retinaSrcFiles.push(filepath);
          return false;
        // Otherwise, keep it in the src files
        } else {
          return true;
        }
      });
      grunt.verbose.writeln('Retina images found: ' + retinaSrcFiles.join(', '));
    }

    // Create an async callback
    var cb = this.async();

    // Determine the format of the image
    var imgOpts = data.imgOpts || {};
    var imgFormat = imgOpts.format || imgFormats.get(destImg) || 'png';

    // Set up the defautls for imgOpts
    _.defaults(imgOpts, {format: imgFormat});

    // Perpare spritesmith parameters
    var spritesmithParams = {
      src: srcFiles,
      engine: data.engine,
      algorithm: data.algorithm,
      padding: data.padding || 0,
      algorithmOpts: data.algorithmOpts || {},
      engineOpts: data.engineOpts || {},
      exportOpts: imgOpts
    };

    // In parallel
    async.parallel([
      // Run our normal task
      function normalSpritesheet (callback) {
        spritesmith(spritesmithParams, callback);
      },
      // If we have a retina task, run it as well
      function retinaSpritesheet (callback) {
        // DEV: We don't check length since we could have no images passed in
        if (retinaSrcFiles) {
          var retinaParams = _.defaults({
            src: retinaSrcFiles
          }, spritesmithParams);
          spritesmith(retinaParams, callback);
        } else {
          process.nextTick(callback);
        }
      }
    ], function handleSpritesheets (err, resultArr) {
      // If an error occurred, callback with it
      if (err) {
        grunt.fatal(err);
        return cb(err);
      }

      // Otherwise, write out the result to destImg
      var result = resultArr[0];
      var destImgDir = path.dirname(destImg);
      grunt.file.mkdir(destImgDir);
      fs.writeFileSync(destImg, result.image, 'binary');

      // Generate a listing of CSS variables
      var coordinates = result.coordinates;
      var properties = result.properties;
      var spritePath = data.imgPath || url.relative(destCss, destImg);
      var spritesheetInfo = {
        width: properties.width,
        height: properties.height,
        image: spritePath
      };
      var cssVarMap = data.cssVarMap || function noop () {};
      var cleanCoords = [];

      // Clean up the file name of the file
      Object.getOwnPropertyNames(coordinates).sort().forEach(function prepareTemplateData (file) {
        // Extract out our name
        var name = getCoordinateName(file);
        var coords = coordinates[file];

        // Specify the image for the sprite
        coords.name = name;
        coords.source_image = file;
        // DEV: `image`, `total_width`, `total_height` are deprecated as they are overwritten in `spritesheet-templates`
        coords.image = spritePath;
        coords.total_width = properties.width;
        coords.total_height = properties.height;

        // Map the coordinates through cssVarMap
        coords = cssVarMap(coords) || coords;

        // Save the cleaned name and coordinates
        cleanCoords.push(coords);
      });

      // If we have retina sprites
      var retinaResult = resultArr[1];
      var retinaSpritesheetInfo;
      if (retinaResult) {
        // Write out the result to destImg
        var retinaDestImgDir = path.dirname(retinaDestImg);
        grunt.file.mkdir(retinaDestImgDir);
        fs.writeFileSync(retinaDestImg, retinaResult.image, 'binary');

        // Generate a listing of CSS variables
        var retinaCoordinates = retinaResult.coordinates;
        var retinaProperties = retinaResult.properties;
        var retinaSpritePath = data.retinaImgPath || url.relative(destCss, retinaDestImg);
        retinaSpritesheetInfo = {
          width: retinaProperties.width,
          height: retinaProperties.height,
          image: retinaSpritePath
        };
        // DEV: We reuse cssVarMap and cleanCoords

        // Clean up the file name of the file
        Object.getOwnPropertyNames(retinaCoordinates).sort().forEach(function prepareRetinaTemplateData (file) {
          var name = getCoordinateName(file);
          var coords = retinaCoordinates[file];
          coords.name = name;
          coords.source_image = file;
          coords.image = retinaSpritePath;
          coords.total_width = retinaProperties.width;
          coords.total_height = retinaProperties.height;
          coords = cssVarMap(coords) || coords;
          cleanCoords.push(coords);
        });
      }

      // If there is a custom template, use it
      var cssFormat = 'spritesmith-custom';
      var cssOptions = data.cssOpts || {};
      if (cssTemplate) {
        if (typeof cssTemplate === 'function') {
          templater.addTemplate(cssFormat, cssTemplate);
        } else {
          templater.addMustacheTemplate(cssFormat, fs.readFileSync(cssTemplate, 'utf8'));
        }
      } else {
      // Otherwise, override the cssFormat and fallback to 'json'
      // TODO: Concatenate on `-retina` to `destCss`
        // cssFormat = data.cssFormat || cssFormats.get(destCss) || 'json';
        cssFormat = 'stylus-retina';
      }

      // Render the variables via `spritesheet-templates`
      // TODO: Move to `hogan.js` and pick up inheritance
      //   https://github.com/twitter/hogan.js
      // TODO: Or maybe handlebars with handlebars-layouts
      //   https://github.com/shannonmoeller/handlebars-layouts
      var cssStr = templater({
        items: cleanCoords,
        spritesheet: spritesheetInfo,
        retinaSpritesheet: retinaSpritesheetInfo
      }, {
        format: cssFormat,
        formatOpts: cssOptions,
        spritesheetName: data.cssSpritesheetName,
        retinaSpritesheetName: data.retinaCssSpritesheetName
      });

      // Write it out to the CSS file
      var destCssDir = path.dirname(destCss);
      grunt.file.mkdir(destCssDir);
      fs.writeFileSync(destCss, cssStr, 'utf8');

      // Fail task if errors were logged.
      if (that.errorCount) { cb(false); }

      // Otherwise, print a success message.
      grunt.log.writeln('Files "' + destCss + '", "' + destImg + '" created.');

      // Callback
      cb(true);
    });
  }

  // Export the SpriteMaker function
  grunt.registerMultiTask('sprite', 'Spritesheet making utility', SpriteMaker);
};
<|endoftext|>


<commit_before>#! /usr/bin/env node
'use strict';

var findup = require("findup-sync");
var fs     = require("fs");
var shell  = require("child_process").execFile;

// Only check the value once
var baseDirectory = (function(){
    return process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE;
}());

var npmrcUserPath = getUserHome() + '/.npmrc';
var npmrcUserBackupPath = getUserHome() + '/.npmrc.bak';

function getPath() {
    var npmrcPath = findup('.npmrc');
    if (npmrcPath) {
        if (npmrcPath !== npmrcUserPath) {
            return npmrcPath;
        }
    } else {
        return null;
    }
}

function renameFile(currentFileName, newFileName) {
    if (fs.existsSync(currentFileName)) {
        fs.renameSync(currentFileName, newFileName);
        if (fs.existsSync(currentFileName)) {
            // You didn't get renamed bro...
            throw new Error("Couldn't rename file.");
        }
    }
}

function copyFile(source, target, callback) {
    shell('/bin/cp', [source, target], function() {
        callback();
    });
}

function returnToDefault() {
    renameFile(npmrcUserBackupPath, npmrcUserPath);
}

function backupCurrent() {
    renameFile(npmrcUserPath, npmrcUserBackupPath);
}

var path = getPath();
if (path) {
    backupCurrent();
    copyFile(path, npmrcUserPath, function() {
    });
} else {
    returnToDefault();
}
<commit_msg>rename variables and use new baseDirectory variable<commit_after>#! /usr/bin/env node
'use strict';

var findup = require("findup-sync");
var fs     = require("fs");
var shell  = require("child_process").execFile;

// Only check the value once
var baseDirectory = (function(){
    return process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE;
}());

var baseFilePath   = baseDirectory + '/.npmrc';
var baseBackupPath = baseDirectory + '/.npmrc.bak';

function getPath() {
    var npmrcPath = findup('.npmrc');
    if (npmrcPath) {
        if (npmrcPath !== baseFilePath) {
            return npmrcPath;
        }
    } else {
        return null;
    }
}

function renameFile(currentFileName, newFileName) {
    if (fs.existsSync(currentFileName)) {
        fs.renameSync(currentFileName, newFileName);
        if (fs.existsSync(currentFileName)) {
            // You didn't get renamed bro...
            throw new Error("Couldn't rename file.");
        }
    }
}

function copyFile(source, target, callback) {
    shell('/bin/cp', [source, target], function() {
        callback();
    });
}

function returnToDefault() {
    renameFile(baseBackupPath, baseFilePath);
}

function backupCurrent() {
    renameFile(baseFilePath, baseBackupPath);
}

var path = getPath();
if (path) {
    backupCurrent();
    copyFile(path, baseFilePath, function() {
    });
} else {
    returnToDefault();
}
<|endoftext|>