Change code to satisfy new ESLint checks
Change-Id: I36926039fff61328626682f37eb229f58e27c928
This commit is contained in:
parent
6062444015
commit
ac6336b5e2
|
@ -3,170 +3,201 @@
|
||||||
*/
|
*/
|
||||||
(function() {
|
(function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
angular
|
||||||
angular.module('merlin')
|
.module('merlin')
|
||||||
/*
|
/*
|
||||||
* Allows to edit field name in-place.
|
* Allows to edit field name in-place.
|
||||||
* For example, you have a field named 'Input 1' and you want to replace this name with "Base input" value.
|
* For example, you have a field named 'Input 1' and you want to replace this name with
|
||||||
|
* "Base input" value.
|
||||||
* If you add editable directive to such field, you will get a marker icon near this field,
|
* If you add editable directive to such field, you will get a marker icon near this field,
|
||||||
* and with clicking on this icon you can type new name and save or discard changes.
|
* and with clicking on this icon you can type new name and save or discard changes.
|
||||||
* */
|
* */
|
||||||
.directive('editable', function() {
|
.directive('editable', editable)
|
||||||
return {
|
|
||||||
restrict: 'E',
|
|
||||||
templateUrl: '/static/merlin/templates/editable.html',
|
|
||||||
require: 'ngModel',
|
|
||||||
scope: true,
|
|
||||||
link: function(scope, element, attrs, ngModelCtrl) {
|
|
||||||
var hiddenSpan = element.find('span.width-detector'),
|
|
||||||
input = element.find('input'),
|
|
||||||
maxWidth = 400;
|
|
||||||
|
|
||||||
function adjustWidth() {
|
/*
|
||||||
var width;
|
* this directive auto-sets the focus to an input field once it is shown.
|
||||||
hiddenSpan.html(scope.editableValue);
|
* */
|
||||||
width = hiddenSpan.width();
|
.directive('showFocus', showFocus)
|
||||||
input.width(width <= maxWidth ? width : maxWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
function accept() {
|
/*
|
||||||
ngModelCtrl.$setViewValue(scope.editableValue);
|
* tells Merlin to render this element as a panel.
|
||||||
scope.isEdited = false;
|
* */
|
||||||
}
|
.directive('panel', panel)
|
||||||
|
|
||||||
function reject() {
|
/*
|
||||||
ngModelCtrl.$rollbackViewValue();
|
* tells Merlin to render this element as a group with ability to collapse.
|
||||||
scope.isEdited = false;
|
* */
|
||||||
}
|
.directive('collapsibleGroup', collapsibleGroup)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sets up the DOM nodes related to validation of model being edited in this widget
|
||||||
|
* (and specifies the name of this model on scope).
|
||||||
|
* */
|
||||||
|
.directive('validatableWith', validatableWith)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* retrieves a template by its name which is the same as model's type and renders it,
|
||||||
|
* recursive <typed-field></..>-s are possible.
|
||||||
|
* */
|
||||||
|
.directive('typedField', typedField);
|
||||||
|
|
||||||
|
typedField.$inject = ['$compile', 'merlin.templates'];
|
||||||
|
|
||||||
|
function editable() {
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
templateUrl: '/static/merlin/templates/editable.html',
|
||||||
|
require: 'ngModel',
|
||||||
|
scope: true,
|
||||||
|
link: function(scope, element, attrs, ngModelCtrl) {
|
||||||
|
var hiddenSpan = element.find('span.width-detector');
|
||||||
|
var input = element.find('input');
|
||||||
|
var maxWidth = 400;
|
||||||
|
|
||||||
|
function adjustWidth() {
|
||||||
|
var width;
|
||||||
|
hiddenSpan.html(scope.editableValue);
|
||||||
|
width = hiddenSpan.width();
|
||||||
|
input.width(width <= maxWidth ? width : maxWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
function accept() {
|
||||||
|
ngModelCtrl.$setViewValue(scope.editableValue);
|
||||||
scope.isEdited = false;
|
scope.isEdited = false;
|
||||||
scope.$watch('editableValue', function() {
|
|
||||||
adjustWidth();
|
|
||||||
});
|
|
||||||
input.on('keyup', function(e) {
|
|
||||||
if ( e.keyCode == 13 ) {
|
|
||||||
accept();
|
|
||||||
scope.$apply();
|
|
||||||
} else if (e.keyCode == 27 ) {
|
|
||||||
reject();
|
|
||||||
scope.$apply();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
ngModelCtrl.$render = function() {
|
|
||||||
if ( !ngModelCtrl.$viewValue ) {
|
|
||||||
ngModelCtrl.$viewValue = ngModelCtrl.$modelValue;
|
|
||||||
}
|
|
||||||
scope.editableValue = ngModelCtrl.$viewValue;
|
|
||||||
adjustWidth();
|
|
||||||
};
|
|
||||||
scope.accept = accept;
|
|
||||||
scope.reject = reject;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
})
|
function reject() {
|
||||||
/*
|
ngModelCtrl.$rollbackViewValue();
|
||||||
* this directive auto-sets the focus to an input field once it is shown.
|
scope.isEdited = false;
|
||||||
* */
|
}
|
||||||
.directive('showFocus', function($timeout) {
|
|
||||||
return function(scope, element, attrs) {
|
scope.isEdited = false;
|
||||||
scope.$watch(attrs.showFocus, function(newValue) {
|
|
||||||
$timeout(function() {
|
// Unused variable created here due to rule 'ng_on_watch': 2
|
||||||
newValue && element.focus();
|
// (see https://github.com/Gillespie59/eslint-plugin-angular)
|
||||||
});
|
var editableValueWatcher = scope.$watch('editableValue', function() {
|
||||||
|
adjustWidth();
|
||||||
});
|
});
|
||||||
|
input.on('keyup', function(e) {
|
||||||
|
if ( e.keyCode == 13 ) {
|
||||||
|
accept();
|
||||||
|
scope.$apply();
|
||||||
|
} else if (e.keyCode == 27 ) {
|
||||||
|
reject();
|
||||||
|
scope.$apply();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ngModelCtrl.$render = function() {
|
||||||
|
if ( !ngModelCtrl.$viewValue ) {
|
||||||
|
ngModelCtrl.$viewValue = ngModelCtrl.$modelValue;
|
||||||
|
}
|
||||||
|
scope.editableValue = ngModelCtrl.$viewValue;
|
||||||
|
adjustWidth();
|
||||||
|
};
|
||||||
|
scope.accept = accept;
|
||||||
|
scope.reject = reject;
|
||||||
}
|
}
|
||||||
})
|
};
|
||||||
/*
|
}
|
||||||
* tells Merlin to render this element as a panel.
|
|
||||||
* */
|
function showFocus($timeout) {
|
||||||
.directive('panel', function($parse) {
|
return function(scope, element, attrs) {
|
||||||
return {
|
// Unused variable created here due to rule 'ng_on_watch': 2
|
||||||
restrict: 'E',
|
// (see https://github.com/Gillespie59/eslint-plugin-angular)
|
||||||
templateUrl: '/static/merlin/templates/collapsible-panel.html',
|
var showFocusWatcher = scope.$watch(attrs.showFocus, function(newValue) {
|
||||||
transclude: true,
|
$timeout(function() {
|
||||||
scope: {
|
if (newValue) {
|
||||||
panel: '=content'
|
element.focus();
|
||||||
},
|
}
|
||||||
link: function(scope, element, attrs) {
|
});
|
||||||
scope.removable = $parse(attrs.removable)();
|
});
|
||||||
scope.isCollapsed = false;
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function panel($parse) {
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
templateUrl: '/static/merlin/templates/collapsible-panel.html',
|
||||||
|
transclude: true,
|
||||||
|
scope: {
|
||||||
|
panel: '=content'
|
||||||
|
},
|
||||||
|
link: function(scope, element, attrs) {
|
||||||
|
scope.removable = $parse(attrs.removable)();
|
||||||
|
scope.isCollapsed = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function collapsibleGroup() {
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
templateUrl: '/static/merlin/templates/collapsible-group.html',
|
||||||
|
transclude: true,
|
||||||
|
scope: {
|
||||||
|
group: '=content',
|
||||||
|
onAdd: '&',
|
||||||
|
onRemove: '&'
|
||||||
|
},
|
||||||
|
link: function(scope, element, attrs) {
|
||||||
|
scope.isCollapsed = false;
|
||||||
|
if ( attrs.onAdd && attrs.additive !== 'false' ) {
|
||||||
|
scope.additive = true;
|
||||||
|
}
|
||||||
|
if ( attrs.onRemove && attrs.removable !== 'false' ) {
|
||||||
|
scope.removable = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
};
|
||||||
/*
|
}
|
||||||
* tells Merlin to render this element as a group with ability to collapse.
|
|
||||||
* */
|
function validatableWith($parse) {
|
||||||
.directive('collapsibleGroup', function() {
|
return {
|
||||||
return {
|
restrict: 'A',
|
||||||
restrict: 'E',
|
require: 'ngModel',
|
||||||
templateUrl: '/static/merlin/templates/collapsible-group.html',
|
link: function(scope, element, attrs, ctrl) {
|
||||||
transclude: true,
|
var model;
|
||||||
scope: {
|
if ( attrs.validatableWith ) {
|
||||||
group: '=content',
|
model = $parse(attrs.validatableWith)(scope);
|
||||||
onAdd: '&',
|
scope.error = '';
|
||||||
onRemove: '&'
|
if (model.setValidatable) {
|
||||||
},
|
model.setValidatable(true);
|
||||||
link: function(scope, element, attrs) {
|
|
||||||
scope.isCollapsed = false;
|
|
||||||
if ( attrs.onAdd && attrs.additive !== 'false' ) {
|
|
||||||
scope.additive = true;
|
|
||||||
}
|
}
|
||||||
if ( attrs.onRemove && attrs.removable !== 'false' ) {
|
if (model.on) {
|
||||||
scope.removable = true;
|
model.on('validation', function(result) {
|
||||||
}
|
var isValid = (result == 'succeeded');
|
||||||
}
|
var baseMessage = '';
|
||||||
}
|
|
||||||
})
|
|
||||||
/*
|
|
||||||
* sets up the DOM nodes related to validation of model being edited in this widget (and specifies the name of this model on scope).
|
|
||||||
* */
|
|
||||||
.directive('validatableWith', function($parse) {
|
|
||||||
return {
|
|
||||||
restrict: 'A',
|
|
||||||
require: 'ngModel',
|
|
||||||
link: function(scope, element, attrs, ctrl) {
|
|
||||||
var model;
|
|
||||||
if ( attrs.validatableWith ) {
|
|
||||||
model = $parse(attrs.validatableWith)(scope);
|
|
||||||
scope.error = '';
|
|
||||||
model.setValidatable && model.setValidatable(true);
|
|
||||||
model.on && model.on('validation', function(result) {
|
|
||||||
var isValid = (result == 'succeeded'),
|
|
||||||
baseMessage = '';
|
|
||||||
// (FIXME): hack until Barricade supports validation of empty required entries
|
// (FIXME): hack until Barricade supports validation of empty required entries
|
||||||
if ( !model.get() && model.isRequired() ) {
|
if ( !model.get() && model.isRequired() ) {
|
||||||
isValid = false;
|
isValid = false;
|
||||||
baseMessage = 'This field is required.'
|
baseMessage = 'This field is required.';
|
||||||
}
|
}
|
||||||
ctrl.$setValidity('barricade', isValid);
|
ctrl.$setValidity('barricade', isValid);
|
||||||
scope.error = model.hasError() ? model.getError() : baseMessage;
|
scope.error = model.hasError() ? model.getError() : baseMessage;
|
||||||
});
|
});
|
||||||
ctrl.$formatters.push(function(modelValue) {
|
|
||||||
return modelValue === undefined ?
|
|
||||||
( ctrl.$isEmpty(ctrl.$viewValue) ? undefined : ctrl.$viewValue ) :
|
|
||||||
modelValue;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
ctrl.$formatters.push(function(modelValue) {
|
||||||
|
return angular.isUndefined(modelValue) ?
|
||||||
|
( ctrl.$isEmpty(ctrl.$viewValue) ? undefined : ctrl.$viewValue ) :
|
||||||
|
modelValue;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
};
|
||||||
/*
|
}
|
||||||
* retrieves a template by its name which is the same as model's type and renders it, recursive <typed-field></..>-s are possible.
|
|
||||||
* */
|
|
||||||
.directive('typedField', ['$compile', 'merlin.templates',
|
|
||||||
function($compile, templates) {
|
|
||||||
return {
|
|
||||||
restrict: 'E',
|
|
||||||
scope: {
|
|
||||||
value: '=',
|
|
||||||
type: '@'
|
|
||||||
},
|
|
||||||
link: function(scope, element) {
|
|
||||||
templates.templateReady(scope.type).then(function(template) {
|
|
||||||
element.replaceWith($compile(template)(scope));
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}])
|
|
||||||
|
|
||||||
|
function typedField($compile, templates) {
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
scope: {
|
||||||
|
value: '=',
|
||||||
|
type: '@'
|
||||||
|
},
|
||||||
|
link: function(scope, element) {
|
||||||
|
templates.templateReady(scope.type).then(function(template) {
|
||||||
|
element.replaceWith($compile(template)(scope));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -2,304 +2,308 @@
|
||||||
(function() {
|
(function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('merlin')
|
angular
|
||||||
.factory('merlin.field.models',
|
.module('merlin')
|
||||||
['merlin.utils', 'merlin.panel.models', '$http', function(utils, panels, $http) {
|
.factory('merlin.field.models', merlinFieldModels);
|
||||||
|
|
||||||
var wildcardMixin = Barricade.Blueprint.create(function() {
|
merlinFieldModels.$inject = ['merlin.utils', 'merlin.panel.models', '$http'];
|
||||||
return this;
|
|
||||||
});
|
|
||||||
|
|
||||||
var viewChoicesMixin = Barricade.Blueprint.create(function() {
|
function merlinFieldModels(utils, panels, $http) {
|
||||||
var self = this,
|
var wildcardMixin = Barricade.Blueprint.create(function() {
|
||||||
dropDownLimit = this._dropDownLimit || 5,
|
return this;
|
||||||
values, labels, items, isDropDown;
|
});
|
||||||
|
|
||||||
function fillItems() {
|
var viewChoicesMixin = Barricade.Blueprint.create(function() {
|
||||||
values = self.getEnumValues();
|
var self = this;
|
||||||
labels = self.getEnumLabels();
|
var dropDownLimit = this._dropDownLimit || 5;
|
||||||
items = {};
|
var values, labels, items, isDropDown;
|
||||||
|
|
||||||
values && values.forEach(function (value, index) {
|
function fillItems() {
|
||||||
|
values = self.getEnumValues();
|
||||||
|
labels = self.getEnumLabels();
|
||||||
|
items = {};
|
||||||
|
|
||||||
|
if (values) {
|
||||||
|
values.forEach(function (value, index) {
|
||||||
items[value] = labels[index];
|
items[value] = labels[index];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getLabel = function(value) {
|
|
||||||
if ( values === undefined ) {
|
|
||||||
fillItems();
|
|
||||||
}
|
|
||||||
return items[value];
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getValues = function() {
|
|
||||||
if ( values === undefined ) {
|
|
||||||
fillItems();
|
|
||||||
}
|
|
||||||
return values;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.resetValues = function() {
|
|
||||||
values = undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.isDropDown = function() {
|
|
||||||
// what starts its life as being dropdown / not being dropdown
|
|
||||||
// should remain so forever
|
|
||||||
if ( angular.isUndefined(isDropDown) ) {
|
|
||||||
isDropDown = !this.isEmpty() && this.getValues().length < dropDownLimit;
|
|
||||||
}
|
|
||||||
return isDropDown;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.setType('choices');
|
|
||||||
return this;
|
|
||||||
});
|
|
||||||
|
|
||||||
var modelMixin = Barricade.Blueprint.create(function(type) {
|
|
||||||
var isValid = true,
|
|
||||||
isValidatable = false;
|
|
||||||
this.value = function() {
|
|
||||||
if ( !arguments.length ) {
|
|
||||||
if ( isValidatable ) {
|
|
||||||
return isValid ? this.get() : undefined;
|
|
||||||
} else {
|
|
||||||
return this.get();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.set(arguments[0]);
|
|
||||||
isValid = !this.hasError();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.id = utils.getNewId();
|
|
||||||
|
|
||||||
this.getType = function() {
|
|
||||||
return type;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.setValidatable = function(validatable) {
|
|
||||||
isValidatable = validatable;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.setType = function(_type) {
|
|
||||||
type = _type;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.isAtomic = function() {
|
|
||||||
return ['number', 'string', 'text', 'choices'].indexOf(this.getType()) > -1;
|
|
||||||
};
|
|
||||||
this.title = function() {
|
|
||||||
var title = utils.getMeta(this, 'title');
|
|
||||||
if ( !title ) {
|
|
||||||
if ( this.instanceof(Barricade.ImmutableObject) ) {
|
|
||||||
if ( this.getKeys().indexOf('name') > -1 ) {
|
|
||||||
return this.get('name').get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
title = utils.makeTitle(this.getID()) || '';
|
|
||||||
}
|
|
||||||
return title;
|
|
||||||
};
|
|
||||||
wildcardMixin.call(this);
|
|
||||||
if ( this.getEnumValues ) {
|
|
||||||
viewChoicesMixin.call(this);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
});
|
|
||||||
|
|
||||||
function meldGroup() {
|
|
||||||
if ( utils.getMeta(this, 'group') ) {
|
|
||||||
panels.groupmixin.call(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var stringModel = Barricade.Primitive.extend({
|
this.getLabel = function(value) {
|
||||||
create: function(json, parameters) {
|
if ( angular.isUndefined(values) ) {
|
||||||
var self = Barricade.Primitive.create.call(this, json, parameters);
|
fillItems();
|
||||||
return modelMixin.call(self, 'string');
|
|
||||||
}
|
}
|
||||||
}, {'@type': String});
|
return items[value];
|
||||||
|
|
||||||
var textModel = Barricade.Primitive.extend({
|
|
||||||
create: function(json, parameters) {
|
|
||||||
var self = Barricade.Primitive.create.call(this, json, parameters);
|
|
||||||
return modelMixin.call(self, 'text');
|
|
||||||
}
|
|
||||||
}, {'@type': String});
|
|
||||||
|
|
||||||
var numberModel = Barricade.Primitive.extend({
|
|
||||||
create: function(json, parameters) {
|
|
||||||
var self = Barricade.Primitive.create.call(this, json, parameters);
|
|
||||||
return modelMixin.call(self, 'number');
|
|
||||||
}
|
|
||||||
}, {'@type': Number});
|
|
||||||
|
|
||||||
var listModel = Barricade.Array.extend({
|
|
||||||
create: function(json, parameters) {
|
|
||||||
var self = Barricade.Array.create.call(this, json, parameters);
|
|
||||||
|
|
||||||
modelMixin.call(self, 'list');
|
|
||||||
|
|
||||||
self.add = function() {
|
|
||||||
self.push(undefined, parameters);
|
|
||||||
};
|
|
||||||
self.getValues = function() {
|
|
||||||
return self.toArray();
|
|
||||||
};
|
|
||||||
self._getContents = function() {
|
|
||||||
return self.toArray();
|
|
||||||
};
|
|
||||||
meldGroup.call(self);
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
}, {'@type': Array});
|
|
||||||
|
|
||||||
var frozendictModel = Barricade.ImmutableObject.extend({
|
|
||||||
create: function(json, parameters) {
|
|
||||||
var self = Barricade.ImmutableObject.create.call(this, json, parameters);
|
|
||||||
self.getKeys().forEach(function(key) {
|
|
||||||
utils.enhanceItemWithID(self.get(key), key);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelMixin.call(self, 'frozendict');
|
|
||||||
self.getValues = function() {
|
|
||||||
return self._data;
|
|
||||||
};
|
|
||||||
self._getContents = function() {
|
|
||||||
return self.getKeys().map(function(key) {
|
|
||||||
return self.get(key);
|
|
||||||
})
|
|
||||||
};
|
|
||||||
meldGroup.call(self);
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
}, {'@type': Object});
|
|
||||||
|
|
||||||
var dictionaryModel = Barricade.MutableObject.extend({
|
|
||||||
create: function(json, parameters) {
|
|
||||||
var self = Barricade.MutableObject.create.call(this, json, parameters),
|
|
||||||
_items = [],
|
|
||||||
_elClass = self._elementClass,
|
|
||||||
baseKey = utils.getMeta(_elClass, 'baseKey') || 'key',
|
|
||||||
baseName = utils.getMeta(_elClass, 'baseName') || utils.makeTitle(baseKey);
|
|
||||||
|
|
||||||
modelMixin.call(self, 'dictionary');
|
|
||||||
|
|
||||||
function makeCacheWrapper(container, key) {
|
|
||||||
var value = container.getByID(key);
|
|
||||||
value.keyValue = function () {
|
|
||||||
if ( arguments.length ) {
|
|
||||||
value.setID(arguments[0]);
|
|
||||||
} else {
|
|
||||||
return value.getID();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.add = function(newID) {
|
|
||||||
var regexp = new RegExp('(' + baseKey + ')([0-9]+)'),
|
|
||||||
newValue;
|
|
||||||
newID = newID || baseKey + utils.getNextIDSuffix(self, regexp);
|
|
||||||
if ( _elClass.instanceof(Barricade.ImmutableObject) ) {
|
|
||||||
if ( 'name' in _elClass._schema ) {
|
|
||||||
var nameNum = utils.getNextIDSuffix(self, regexp);
|
|
||||||
newValue = {name: baseName + nameNum};
|
|
||||||
} else {
|
|
||||||
newValue = {};
|
|
||||||
}
|
|
||||||
} else { // usually, it's either frozendict inside or string
|
|
||||||
newValue = '';
|
|
||||||
}
|
|
||||||
self.push(newValue, utils.extend(self._parameters, {id: newID}));
|
|
||||||
_items.push(makeCacheWrapper(self, newID));
|
|
||||||
};
|
|
||||||
self.getValues = function() {
|
|
||||||
if ( !_items.length ) {
|
|
||||||
_items = self.toArray().map(function(value) {
|
|
||||||
return makeCacheWrapper(self, value.getID());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return _items;
|
|
||||||
};
|
|
||||||
self.empty = function() {
|
|
||||||
for ( var i = this._data.length; i > 0; i-- ) {
|
|
||||||
self.remove(i-1);
|
|
||||||
}
|
|
||||||
_items = [];
|
|
||||||
};
|
|
||||||
self.resetKeys = function(keys) {
|
|
||||||
self.empty();
|
|
||||||
keys.forEach(function(key) {
|
|
||||||
self.push(undefined, {id: key});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
self._getContents = function() {
|
|
||||||
return self.toArray();
|
|
||||||
};
|
|
||||||
self.removeItem = function(key) {
|
|
||||||
var pos = self.getPosByID(key);
|
|
||||||
self.remove(self.getPosByID(key));
|
|
||||||
_items.splice(pos, 1);
|
|
||||||
};
|
|
||||||
meldGroup.call(self);
|
|
||||||
// initialize cache with starting values
|
|
||||||
self.getValues();
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
}, {'@type': Object});
|
|
||||||
|
|
||||||
var linkedCollectionModel = stringModel.extend({
|
|
||||||
create: function(json, parameters) {
|
|
||||||
var self = stringModel.create.call(this, json, parameters),
|
|
||||||
collectionCls = Barricade.create({
|
|
||||||
'@type': String,
|
|
||||||
'@ref': {
|
|
||||||
to: function() {
|
|
||||||
return parameters.toCls;
|
|
||||||
},
|
|
||||||
needs: function() {
|
|
||||||
return parameters.neededCls;
|
|
||||||
},
|
|
||||||
getter: function(data) {
|
|
||||||
return data.needed.get(parameters.substitutedEntryID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
self._collection = collectionCls.create().on(
|
|
||||||
'replace', function(newValue) {
|
|
||||||
self._collection = newValue;
|
|
||||||
self._collection.on('change', function() {
|
|
||||||
self._choices = self._collection.getIDs();
|
|
||||||
self.resetValues();
|
|
||||||
});
|
|
||||||
self._collection.emit('change');
|
|
||||||
});
|
|
||||||
|
|
||||||
return self;
|
|
||||||
},
|
|
||||||
_choices: []
|
|
||||||
}, {
|
|
||||||
'@enum': function() {
|
|
||||||
if ( this._collection.isPlaceholder() ) {
|
|
||||||
this.emit('_resolveUp', this._collection);
|
|
||||||
}
|
|
||||||
return this._choices;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
string: stringModel,
|
|
||||||
text: textModel,
|
|
||||||
number: numberModel,
|
|
||||||
list: listModel,
|
|
||||||
linkedcollection: linkedCollectionModel,
|
|
||||||
dictionary: dictionaryModel,
|
|
||||||
frozendict: frozendictModel,
|
|
||||||
wildcard: wildcardMixin // use for most general type-checks
|
|
||||||
};
|
};
|
||||||
}])
|
|
||||||
|
|
||||||
|
this.getValues = function() {
|
||||||
|
if ( angular.isUndefined(values) ) {
|
||||||
|
fillItems();
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.resetValues = function() {
|
||||||
|
values = undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.isDropDown = function() {
|
||||||
|
// what starts its life as being dropdown / not being dropdown
|
||||||
|
// should remain so forever
|
||||||
|
if ( angular.isUndefined(isDropDown) ) {
|
||||||
|
isDropDown = !this.isEmpty() && this.getValues().length < dropDownLimit;
|
||||||
|
}
|
||||||
|
return isDropDown;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.setType('choices');
|
||||||
|
return this;
|
||||||
|
});
|
||||||
|
|
||||||
|
var modelMixin = Barricade.Blueprint.create(function(type) {
|
||||||
|
var isValid = true;
|
||||||
|
var isValidatable = false;
|
||||||
|
this.value = function() {
|
||||||
|
if ( !arguments.length ) {
|
||||||
|
if ( isValidatable ) {
|
||||||
|
return isValid ? this.get() : undefined;
|
||||||
|
} else {
|
||||||
|
return this.get();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.set(arguments[0]);
|
||||||
|
isValid = !this.hasError();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.id = utils.getNewId();
|
||||||
|
|
||||||
|
this.getType = function() {
|
||||||
|
return type;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.setValidatable = function(validatable) {
|
||||||
|
isValidatable = validatable;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.setType = function(_type) {
|
||||||
|
type = _type;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.isAtomic = function() {
|
||||||
|
return ['number', 'string', 'text', 'choices'].indexOf(this.getType()) > -1;
|
||||||
|
};
|
||||||
|
this.title = function() {
|
||||||
|
var title = utils.getMeta(this, 'title');
|
||||||
|
if ( !title ) {
|
||||||
|
if ( this.instanceof(Barricade.ImmutableObject) ) {
|
||||||
|
if ( this.getKeys().indexOf('name') > -1 ) {
|
||||||
|
return this.get('name').get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
title = utils.makeTitle(this.getID()) || '';
|
||||||
|
}
|
||||||
|
return title;
|
||||||
|
};
|
||||||
|
wildcardMixin.call(this);
|
||||||
|
if ( this.getEnumValues ) {
|
||||||
|
viewChoicesMixin.call(this);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
});
|
||||||
|
|
||||||
|
function meldGroup() {
|
||||||
|
if ( utils.getMeta(this, 'group') ) {
|
||||||
|
panels.groupmixin.call(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var stringModel = Barricade.Primitive.extend({
|
||||||
|
create: function(json, parameters) {
|
||||||
|
var self = Barricade.Primitive.create.call(this, json, parameters);
|
||||||
|
return modelMixin.call(self, 'string');
|
||||||
|
}
|
||||||
|
}, {'@type': String});
|
||||||
|
|
||||||
|
var textModel = Barricade.Primitive.extend({
|
||||||
|
create: function(json, parameters) {
|
||||||
|
var self = Barricade.Primitive.create.call(this, json, parameters);
|
||||||
|
return modelMixin.call(self, 'text');
|
||||||
|
}
|
||||||
|
}, {'@type': String});
|
||||||
|
|
||||||
|
var numberModel = Barricade.Primitive.extend({
|
||||||
|
create: function(json, parameters) {
|
||||||
|
var self = Barricade.Primitive.create.call(this, json, parameters);
|
||||||
|
return modelMixin.call(self, 'number');
|
||||||
|
}
|
||||||
|
}, {'@type': Number});
|
||||||
|
|
||||||
|
var listModel = Barricade.Array.extend({
|
||||||
|
create: function(json, parameters) {
|
||||||
|
var self = Barricade.Array.create.call(this, json, parameters);
|
||||||
|
|
||||||
|
modelMixin.call(self, 'list');
|
||||||
|
|
||||||
|
self.add = function() {
|
||||||
|
self.push(undefined, parameters);
|
||||||
|
};
|
||||||
|
self.getValues = function() {
|
||||||
|
return self.toArray();
|
||||||
|
};
|
||||||
|
self._getContents = function() {
|
||||||
|
return self.toArray();
|
||||||
|
};
|
||||||
|
meldGroup.call(self);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
}, {'@type': Array});
|
||||||
|
|
||||||
|
var frozendictModel = Barricade.ImmutableObject.extend({
|
||||||
|
create: function(json, parameters) {
|
||||||
|
var self = Barricade.ImmutableObject.create.call(this, json, parameters);
|
||||||
|
self.getKeys().forEach(function(key) {
|
||||||
|
utils.enhanceItemWithID(self.get(key), key);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelMixin.call(self, 'frozendict');
|
||||||
|
self.getValues = function() {
|
||||||
|
return self._data;
|
||||||
|
};
|
||||||
|
self._getContents = function() {
|
||||||
|
return self.getKeys().map(function(key) {
|
||||||
|
return self.get(key);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
meldGroup.call(self);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
}, {'@type': Object});
|
||||||
|
|
||||||
|
var dictionaryModel = Barricade.MutableObject.extend({
|
||||||
|
create: function(json, parameters) {
|
||||||
|
var self = Barricade.MutableObject.create.call(this, json, parameters);
|
||||||
|
var _items = [];
|
||||||
|
var _elClass = self._elementClass;
|
||||||
|
var baseKey = utils.getMeta(_elClass, 'baseKey') || 'key';
|
||||||
|
var baseName = utils.getMeta(_elClass, 'baseName') || utils.makeTitle(baseKey);
|
||||||
|
|
||||||
|
modelMixin.call(self, 'dictionary');
|
||||||
|
|
||||||
|
function makeCacheWrapper(container, key) {
|
||||||
|
var value = container.getByID(key);
|
||||||
|
value.keyValue = function () {
|
||||||
|
if ( arguments.length ) {
|
||||||
|
value.setID(arguments[0]);
|
||||||
|
} else {
|
||||||
|
return value.getID();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.add = function(newID) {
|
||||||
|
var regexp = new RegExp('(' + baseKey + ')([0-9]+)');
|
||||||
|
var newValue;
|
||||||
|
newID = newID || baseKey + utils.getNextIDSuffix(self, regexp);
|
||||||
|
if ( _elClass.instanceof(Barricade.ImmutableObject) ) {
|
||||||
|
if ( 'name' in _elClass._schema ) {
|
||||||
|
var nameNum = utils.getNextIDSuffix(self, regexp);
|
||||||
|
newValue = {name: baseName + nameNum};
|
||||||
|
} else {
|
||||||
|
newValue = {};
|
||||||
|
}
|
||||||
|
} else { // usually, it's either frozendict inside or string
|
||||||
|
newValue = '';
|
||||||
|
}
|
||||||
|
self.push(newValue, utils.extend(self._parameters, {id: newID}));
|
||||||
|
_items.push(makeCacheWrapper(self, newID));
|
||||||
|
};
|
||||||
|
self.getValues = function() {
|
||||||
|
if ( !_items.length ) {
|
||||||
|
_items = self.toArray().map(function(value) {
|
||||||
|
return makeCacheWrapper(self, value.getID());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return _items;
|
||||||
|
};
|
||||||
|
self.empty = function() {
|
||||||
|
for ( var i = this._data.length; i > 0; i-- ) {
|
||||||
|
self.remove(i - 1);
|
||||||
|
}
|
||||||
|
_items = [];
|
||||||
|
};
|
||||||
|
self.resetKeys = function(keys) {
|
||||||
|
self.empty();
|
||||||
|
keys.forEach(function(key) {
|
||||||
|
self.push(undefined, {id: key});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
self._getContents = function() {
|
||||||
|
return self.toArray();
|
||||||
|
};
|
||||||
|
self.removeItem = function(key) {
|
||||||
|
var pos = self.getPosByID(key);
|
||||||
|
self.remove(self.getPosByID(key));
|
||||||
|
_items.splice(pos, 1);
|
||||||
|
};
|
||||||
|
meldGroup.call(self);
|
||||||
|
// initialize cache with starting values
|
||||||
|
self.getValues();
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
}, {'@type': Object});
|
||||||
|
|
||||||
|
var linkedCollectionModel = stringModel.extend({
|
||||||
|
create: function(json, parameters) {
|
||||||
|
var self = stringModel.create.call(this, json, parameters);
|
||||||
|
var collectionCls = Barricade.create({
|
||||||
|
'@type': String,
|
||||||
|
'@ref': {
|
||||||
|
to: function() {
|
||||||
|
return parameters.toCls;
|
||||||
|
},
|
||||||
|
needs: function() {
|
||||||
|
return parameters.neededCls;
|
||||||
|
},
|
||||||
|
getter: function(data) {
|
||||||
|
return data.needed.get(parameters.substitutedEntryID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
self._collection = collectionCls.create().on(
|
||||||
|
'replace', function(newValue) {
|
||||||
|
self._collection = newValue;
|
||||||
|
self._collection.on('change', function() {
|
||||||
|
self._choices = self._collection.getIDs();
|
||||||
|
self.resetValues();
|
||||||
|
});
|
||||||
|
self._collection.emit('change');
|
||||||
|
});
|
||||||
|
|
||||||
|
return self;
|
||||||
|
},
|
||||||
|
_choices: []
|
||||||
|
}, {
|
||||||
|
'@enum': function() {
|
||||||
|
if ( this._collection.isPlaceholder() ) {
|
||||||
|
this.emit('_resolveUp', this._collection);
|
||||||
|
}
|
||||||
|
return this._choices;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
string: stringModel,
|
||||||
|
text: textModel,
|
||||||
|
number: numberModel,
|
||||||
|
list: listModel,
|
||||||
|
linkedcollection: linkedCollectionModel,
|
||||||
|
dictionary: dictionaryModel,
|
||||||
|
frozendict: frozendictModel,
|
||||||
|
wildcard: wildcardMixin // use for most general type-checks
|
||||||
|
};
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -14,143 +14,150 @@
|
||||||
under the License.
|
under the License.
|
||||||
*/
|
*/
|
||||||
(function() {
|
(function() {
|
||||||
angular.module('merlin')
|
angular
|
||||||
|
.module('merlin')
|
||||||
|
.filter('extractPanels', extractPanels)
|
||||||
|
.filter('extractRows', extractRows)
|
||||||
|
.filter('extractItems', extractItems);
|
||||||
|
|
||||||
.filter('extractPanels', ['merlin.utils', function(utils) {
|
extractPanels.$inject = ['merlin.utils'];
|
||||||
var panelProto = {
|
extractRows.$inject = ['merlin.utils'];
|
||||||
create: function(itemsOrContainer, id) {
|
extractItems.$inject = ['merlin.utils'];
|
||||||
if ( angular.isArray(itemsOrContainer) && !itemsOrContainer.length ) {
|
|
||||||
return null;
|
function extractPanels(utils) {
|
||||||
}
|
var panelProto = {
|
||||||
if ( angular.isArray(itemsOrContainer) ) {
|
create: function(itemsOrContainer, id) {
|
||||||
this.items = itemsOrContainer;
|
if ( angular.isArray(itemsOrContainer) && !itemsOrContainer.length ) {
|
||||||
this.id = itemsOrContainer.reduce(function(prevId, item) {
|
return null;
|
||||||
return item.uid() + prevId;
|
}
|
||||||
}, '');
|
if ( angular.isArray(itemsOrContainer) ) {
|
||||||
|
this.items = itemsOrContainer;
|
||||||
|
this.id = itemsOrContainer.reduce(function(prevId, item) {
|
||||||
|
return item.uid() + prevId;
|
||||||
|
}, '');
|
||||||
|
} else {
|
||||||
|
this._barricadeContainer = itemsOrContainer;
|
||||||
|
this._barricadeId = id;
|
||||||
|
var barricadeObj = itemsOrContainer.getByID(id);
|
||||||
|
this.id = barricadeObj.uid();
|
||||||
|
this.items = barricadeObj.getKeys().map(function(key) {
|
||||||
|
return utils.enhanceItemWithID(barricadeObj.get(key), key);
|
||||||
|
});
|
||||||
|
this.removable = true;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
title: function() {
|
||||||
|
var newID;
|
||||||
|
if ( this._barricadeContainer ) {
|
||||||
|
if ( arguments.length ) {
|
||||||
|
newID = arguments[0];
|
||||||
|
this._barricadeContainer.getByID(this._barricadeId).setID(newID);
|
||||||
|
this._barricadeId = newID;
|
||||||
} else {
|
} else {
|
||||||
this._barricadeContainer = itemsOrContainer;
|
return this._barricadeId;
|
||||||
this._barricadeId = id;
|
|
||||||
var barricadeObj = itemsOrContainer.getByID(id);
|
|
||||||
this.id = barricadeObj.uid();
|
|
||||||
this.items = barricadeObj.getKeys().map(function(key) {
|
|
||||||
return utils.enhanceItemWithID(barricadeObj.get(key), key);
|
|
||||||
});
|
|
||||||
this.removable = true;
|
|
||||||
}
|
}
|
||||||
return this;
|
|
||||||
},
|
|
||||||
title: function() {
|
|
||||||
var newID;
|
|
||||||
if ( this._barricadeContainer ) {
|
|
||||||
if ( arguments.length ) {
|
|
||||||
newID = arguments[0];
|
|
||||||
this._barricadeContainer.getByID(this._barricadeId).setID(newID);
|
|
||||||
this._barricadeId = newID;
|
|
||||||
} else {
|
|
||||||
return this._barricadeId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
remove: function() {
|
|
||||||
var container = this._barricadeContainer,
|
|
||||||
pos = container.getPosByID(this._barricadeId);
|
|
||||||
container.remove(pos);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function isPanelsRoot(item) {
|
|
||||||
try {
|
|
||||||
// check for 'actions' and 'workflows' containers
|
|
||||||
return item.instanceof(Barricade.MutableObject);
|
|
||||||
}
|
|
||||||
catch(err) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
remove: function() {
|
||||||
|
var container = this._barricadeContainer;
|
||||||
|
var pos = container.getPosByID(this._barricadeId);
|
||||||
|
container.remove(pos);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function extractPanelsRoot(items) {
|
function isPanelsRoot(item) {
|
||||||
return isPanelsRoot(items[0]) ? items[0] : null;
|
try {
|
||||||
|
// check for 'actions' and 'workflows' containers
|
||||||
|
return item.instanceof(Barricade.MutableObject);
|
||||||
}
|
}
|
||||||
|
catch(err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return _.memoize(function(container) {
|
function extractPanelsRoot(items) {
|
||||||
var items = container._getContents(),
|
return isPanelsRoot(items[0]) ? items[0] : null;
|
||||||
panels = [];
|
}
|
||||||
utils.groupByMetaKey(items, 'panelIndex').forEach(function(items) {
|
|
||||||
var panelsRoot = extractPanelsRoot(items);
|
|
||||||
if ( panelsRoot ) {
|
|
||||||
panelsRoot.getIDs().forEach(function(id) {
|
|
||||||
panels.push(Object.create(panelProto).create(panelsRoot, id));
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
panels.push(Object.create(panelProto).create(items));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return panels.condense();
|
|
||||||
}, function(container) {
|
|
||||||
var hash = '';
|
|
||||||
container.getKeys().map(function(key) {
|
|
||||||
var item = container.get(key);
|
|
||||||
if ( isPanelsRoot(item) ) {
|
|
||||||
item.getIDs().forEach(function(id) {
|
|
||||||
hash += item.getByID(id).uid();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
hash += item.uid();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return hash;
|
|
||||||
});
|
|
||||||
}])
|
|
||||||
|
|
||||||
.filter('extractRows', ['merlin.utils', function(utils) {
|
return _.memoize(function(container) {
|
||||||
function getItems(panelOrContainer) {
|
var items = container._getContents();
|
||||||
if ( panelOrContainer.items ) {
|
var panels = [];
|
||||||
return panelOrContainer.items;
|
utils.groupByMetaKey(items, 'panelIndex').forEach(function(items) {
|
||||||
} else if ( panelOrContainer.getKeys ) {
|
var panelsRoot = extractPanelsRoot(items);
|
||||||
return panelOrContainer.getKeys().map(function(key) {
|
if ( panelsRoot ) {
|
||||||
return panelOrContainer.get(key);
|
panelsRoot.getIDs().forEach(function(id) {
|
||||||
|
panels.push(Object.create(panelProto).create(panelsRoot, id));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return panelOrContainer.getIDs().map(function(id) {
|
panels.push(Object.create(panelProto).create(items));
|
||||||
return panelOrContainer.getByID(id);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
return utils.condense(panels);
|
||||||
|
}, function(container) {
|
||||||
|
var hash = '';
|
||||||
|
container.getKeys().map(function(key) {
|
||||||
|
var item = container.get(key);
|
||||||
|
if ( isPanelsRoot(item) ) {
|
||||||
|
item.getIDs().forEach(function(id) {
|
||||||
|
hash += item.getByID(id).uid();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
hash += item.uid();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return hash;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractRows(utils) {
|
||||||
|
function getItems(panelOrContainer) {
|
||||||
|
if ( panelOrContainer.items ) {
|
||||||
|
return panelOrContainer.items;
|
||||||
|
} else if ( panelOrContainer.getKeys ) {
|
||||||
|
return panelOrContainer.getKeys().map(function(key) {
|
||||||
|
return panelOrContainer.get(key);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return panelOrContainer.getIDs().map(function(id) {
|
||||||
|
return panelOrContainer.getByID(id);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return _.memoize(function(panel) {
|
return _.memoize(function(panel) {
|
||||||
var rowProto = {
|
var rowProto = {
|
||||||
create: function(items) {
|
create: function(items) {
|
||||||
this.id = items[0].uid();
|
this.id = items[0].uid();
|
||||||
this.index = items.row;
|
this.index = items.row;
|
||||||
this.items = items.slice();
|
this.items = items.slice();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return utils.groupByMetaKey(getItems(panel), 'row').map(function(items) {
|
return utils.groupByMetaKey(getItems(panel), 'row').map(function(items) {
|
||||||
return Object.create(rowProto).create(items);
|
return Object.create(rowProto).create(items);
|
||||||
});
|
});
|
||||||
}, function(panel) {
|
}, function(panel) {
|
||||||
var hash = '';
|
var hash = '';
|
||||||
getItems(panel).forEach(function(item) {
|
getItems(panel).forEach(function(item) {
|
||||||
hash += item.uid();
|
hash += item.uid();
|
||||||
});
|
});
|
||||||
return hash;
|
return hash;
|
||||||
})
|
});
|
||||||
}])
|
}
|
||||||
|
|
||||||
.filter('extractItems', ['merlin.utils', function(utils) {
|
|
||||||
return _.memoize(function(row) {
|
|
||||||
return row.items.sort(function(item1, item2) {
|
|
||||||
return utils.getMeta(item1, 'index') - utils.getMeta(item2, 'index');
|
|
||||||
});
|
|
||||||
}, function(row) {
|
|
||||||
var hash = '';
|
|
||||||
row.items.forEach(function(item) {
|
|
||||||
hash += item.uid();
|
|
||||||
});
|
|
||||||
return hash;
|
|
||||||
})
|
|
||||||
}])
|
|
||||||
|
|
||||||
|
function extractItems(utils) {
|
||||||
|
return _.memoize(function(row) {
|
||||||
|
return row.items.sort(function(item1, item2) {
|
||||||
|
return utils.getMeta(item1, 'index') - utils.getMeta(item2, 'index');
|
||||||
|
});
|
||||||
|
}, function(row) {
|
||||||
|
var hash = '';
|
||||||
|
row.items.forEach(function(item) {
|
||||||
|
hash += item.uid();
|
||||||
|
});
|
||||||
|
return hash;
|
||||||
|
});
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -4,20 +4,33 @@
|
||||||
(function() {
|
(function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('merlin', ['ui.bootstrap'])
|
angular
|
||||||
.config(function($interpolateProvider) {
|
.module('merlin', ['ui.bootstrap'])
|
||||||
// Replacing the default angular symbol
|
.config(interpolateProvider)
|
||||||
// allow us to mix angular with django templates
|
|
||||||
$interpolateProvider.startSymbol('{$');
|
|
||||||
$interpolateProvider.endSymbol('$}');
|
|
||||||
})
|
|
||||||
// move these 2 values out of run section to change them in unit-tests
|
// move these 2 values out of run section to change them in unit-tests
|
||||||
.value('fieldTemplatesUrl', '/static/merlin/templates/fields/')
|
.value('fieldTemplatesUrl', '/static/merlin/templates/fields/')
|
||||||
.value('fieldTemplates', ['dictionary', 'frozendict', 'list',
|
// The false posititive on array constant here we're working around is caused
|
||||||
'string', 'text', 'group', 'number', 'choices'])
|
// by https://github.com/Gillespie59/eslint-plugin-angular/issues/99
|
||||||
.run(['merlin.templates', 'fieldTemplatesUrl', 'fieldTemplates',
|
.value('fieldTemplates', fieldTemplates())
|
||||||
function(templates, rootUrl, fieldList) {
|
.run(runTemplates);
|
||||||
templates.prefetch(rootUrl, fieldList);
|
|
||||||
}])
|
|
||||||
|
|
||||||
})();
|
runTemplates.$inject = ['merlin.templates', 'fieldTemplatesUrl', 'fieldTemplates'];
|
||||||
|
|
||||||
|
function fieldTemplates() {
|
||||||
|
return [
|
||||||
|
'dictionary', 'frozendict', 'list',
|
||||||
|
'string', 'text', 'group', 'number', 'choices'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function runTemplates(templates, rootUrl, fieldList) {
|
||||||
|
templates.prefetch(rootUrl, fieldList);
|
||||||
|
}
|
||||||
|
|
||||||
|
function interpolateProvider($interpolateProvider) {
|
||||||
|
// Replacing the default angular symbol
|
||||||
|
// allow us to mix angular with django templates
|
||||||
|
$interpolateProvider.startSymbol('{$');
|
||||||
|
$interpolateProvider.endSymbol('$}');
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
|
@ -4,46 +4,49 @@
|
||||||
(function() {
|
(function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('merlin')
|
angular
|
||||||
.factory('merlin.panel.models', ['merlin.utils', function(utils) {
|
.module('merlin')
|
||||||
|
.factory('merlin.panel.models', merlinPanelModels);
|
||||||
|
|
||||||
var groupMixin = Barricade.Blueprint.create(function() {
|
merlinPanelModels.$inject = ['merlin.utils'];
|
||||||
var self = this,
|
|
||||||
additive = utils.getMeta(self, 'additive'),
|
|
||||||
removable = utils.getMeta(self, 'removable');
|
|
||||||
|
|
||||||
if ( additive === undefined ) {
|
function merlinPanelModels(utils) {
|
||||||
additive = true;
|
var groupMixin = Barricade.Blueprint.create(function() {
|
||||||
}
|
var self = this;
|
||||||
self.isAdditive = function() {
|
var additive = utils.getMeta(self, 'additive');
|
||||||
return additive;
|
var removable = utils.getMeta(self, 'removable');
|
||||||
};
|
|
||||||
|
|
||||||
if ( removable === undefined ) {
|
if ( angular.isUndefined(additive) ) {
|
||||||
removable = false;
|
additive = true;
|
||||||
}
|
|
||||||
self.isRemovable = function() {
|
|
||||||
return removable;
|
|
||||||
};
|
|
||||||
|
|
||||||
if ( removable ) { // conditionally override common .title()
|
|
||||||
self.title = function() {
|
|
||||||
if ( arguments.length ) {
|
|
||||||
self.setID(arguments[0]);
|
|
||||||
} else {
|
|
||||||
return self.getID();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.setType('group');
|
|
||||||
|
|
||||||
return self;
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
groupmixin: groupMixin
|
|
||||||
}
|
}
|
||||||
}])
|
self.isAdditive = function() {
|
||||||
|
return additive;
|
||||||
|
};
|
||||||
|
|
||||||
})();
|
if ( angular.isUndefined(removable) ) {
|
||||||
|
removable = false;
|
||||||
|
}
|
||||||
|
self.isRemovable = function() {
|
||||||
|
return removable;
|
||||||
|
};
|
||||||
|
|
||||||
|
if ( removable ) { // conditionally override common .title()
|
||||||
|
self.title = function() {
|
||||||
|
if ( arguments.length ) {
|
||||||
|
self.setID(arguments[0]);
|
||||||
|
} else {
|
||||||
|
return self.getID();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
self.setType('group');
|
||||||
|
|
||||||
|
return self;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
groupmixin: groupMixin
|
||||||
|
};
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
|
@ -1,37 +1,41 @@
|
||||||
(function() {
|
(function() {
|
||||||
angular.module('merlin')
|
angular
|
||||||
.factory('merlin.templates', [
|
.module('merlin')
|
||||||
'$http', '$q', function($http, $q) {
|
.factory('merlin.templates', merlinTemplates);
|
||||||
var promises = {};
|
|
||||||
|
|
||||||
function makeEmptyPromise() {
|
merlinTemplates.$inject = ['$http', '$q'];
|
||||||
var deferred = $q.defer();
|
|
||||||
deferred.reject();
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
function prefetch(baseUrl, fields) {
|
function merlinTemplates($http, $q) {
|
||||||
if ( !angular.isArray(fields) ) {
|
var promises = {};
|
||||||
fields = [fields];
|
|
||||||
}
|
|
||||||
fields.forEach(function(field) {
|
|
||||||
var deferred = $q.defer();
|
|
||||||
$http.get(baseUrl + field + '.html').success(function(templateContent) {
|
|
||||||
deferred.resolve(templateContent);
|
|
||||||
}).error(function(data) {
|
|
||||||
deferred.reject(data);
|
|
||||||
});
|
|
||||||
promises[field] = deferred.promise;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function templateReady(field) {
|
function makeEmptyPromise() {
|
||||||
return promises[field] || makeEmptyPromise();
|
var deferred = $q.defer();
|
||||||
}
|
deferred.reject();
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
function prefetch(baseUrl, fields) {
|
||||||
prefetch: prefetch,
|
if ( !angular.isArray(fields) ) {
|
||||||
templateReady: templateReady
|
fields = [fields];
|
||||||
};
|
}
|
||||||
}])
|
fields.forEach(function(field) {
|
||||||
})();
|
var deferred = $q.defer();
|
||||||
|
$http.get(baseUrl + field + '.html').success(function(templateContent) {
|
||||||
|
deferred.resolve(templateContent);
|
||||||
|
}).error(function(data) {
|
||||||
|
deferred.reject(data);
|
||||||
|
});
|
||||||
|
promises[field] = deferred.promise;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function templateReady(field) {
|
||||||
|
return promises[field] || makeEmptyPromise();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
prefetch: prefetch,
|
||||||
|
templateReady: templateReady
|
||||||
|
};
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
|
@ -1,109 +1,115 @@
|
||||||
/**
|
/**
|
||||||
* Created by tsufiev on 2/24/15.
|
* Created by tsufiev on 2/24/15.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('merlin')
|
angular
|
||||||
.factory('merlin.utils', function() {
|
.module('merlin')
|
||||||
Array.prototype.condense = function() {
|
.factory('merlin.utils', merlinUtils);
|
||||||
return this.filter(function(el) {
|
|
||||||
return el !== undefined && el != null;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var _id_counter = 0;
|
function merlinUtils() {
|
||||||
|
function condense(array) {
|
||||||
|
return array.filter(function(el) {
|
||||||
|
return angular.isDefined(el) && el !== null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function getNewId() {
|
var idCounter = 0;
|
||||||
_id_counter++;
|
|
||||||
return 'id-' + _id_counter;
|
|
||||||
}
|
|
||||||
|
|
||||||
function groupByMetaKey(sequence, metaKey, insertAtBeginning) {
|
function getNewId() {
|
||||||
var newSequence = [], defaultBucket = [],
|
idCounter++;
|
||||||
index;
|
return 'id-' + idCounter;
|
||||||
sequence.forEach(function(item) {
|
}
|
||||||
index = getMeta(item, metaKey);
|
|
||||||
if ( index !== undefined ) {
|
function groupByMetaKey(sequence, metaKey, insertAtBeginning) {
|
||||||
if ( !newSequence[index] ) {
|
var newSequence = [];
|
||||||
newSequence[index] = [];
|
var defaultBucket = [];
|
||||||
newSequence[index][metaKey] = index;
|
var index;
|
||||||
}
|
sequence.forEach(function(item) {
|
||||||
newSequence[index].push(item);
|
index = getMeta(item, metaKey);
|
||||||
} else {
|
if ( angular.isDefined(index) ) {
|
||||||
defaultBucket.push(item);
|
if ( !newSequence[index] ) {
|
||||||
|
newSequence[index] = [];
|
||||||
|
newSequence[index][metaKey] = index;
|
||||||
}
|
}
|
||||||
});
|
newSequence[index].push(item);
|
||||||
newSequence = newSequence.condense();
|
|
||||||
// insert default bucket at the beginning/end of sequence
|
|
||||||
if ( defaultBucket.length ) {
|
|
||||||
if ( insertAtBeginning ) {
|
|
||||||
newSequence.splice(0, 0, defaultBucket);
|
|
||||||
} else {
|
|
||||||
newSequence.push(defaultBucket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return newSequence;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMeta(item, key) {
|
|
||||||
if ( item ) {
|
|
||||||
var meta = item._schema['@meta'];
|
|
||||||
return meta && meta[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeTitle(str) {
|
|
||||||
if ( !str ) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
var firstLetter = str.substr(0, 1).toUpperCase();
|
|
||||||
return firstLetter + str.substr(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getNextIDSuffix(container, regexp) {
|
|
||||||
var max = Math.max.apply(Math, container.getIDs().map(function(id) {
|
|
||||||
var match = regexp.exec(id);
|
|
||||||
return match && +match[2];
|
|
||||||
}));
|
|
||||||
return max > 0 ? max + 1 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
function enhanceItemWithID(item, id) {
|
|
||||||
item.setID(id);
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
function pop(obj, key) {
|
|
||||||
if ( obj.hasOwnProperty(key) ) {
|
|
||||||
var value = obj[key];
|
|
||||||
delete obj[key];
|
|
||||||
return value;
|
|
||||||
} else {
|
} else {
|
||||||
return undefined;
|
defaultBucket.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
newSequence = condense(newSequence);
|
||||||
|
// insert default bucket at the beginning/end of sequence
|
||||||
|
if ( defaultBucket.length ) {
|
||||||
|
if ( insertAtBeginning ) {
|
||||||
|
newSequence.splice(0, 0, defaultBucket);
|
||||||
|
} else {
|
||||||
|
newSequence.push(defaultBucket);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return newSequence;
|
||||||
|
}
|
||||||
|
|
||||||
function extend(proto, extension) {
|
function getMeta(item, key) {
|
||||||
var newObj;
|
if ( item ) {
|
||||||
proto = (proto !== undefined ? proto : null);
|
var meta = item._schema['@meta'];
|
||||||
newObj = Object.create(proto);
|
return meta && meta[key];
|
||||||
Object.keys(extension).forEach(function(key) {
|
|
||||||
newObj[key] = extension[key];
|
|
||||||
});
|
|
||||||
return newObj;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
function makeTitle(str) {
|
||||||
getMeta: getMeta,
|
if ( !str ) {
|
||||||
getNewId: getNewId,
|
return '';
|
||||||
groupByMetaKey: groupByMetaKey,
|
|
||||||
makeTitle: makeTitle,
|
|
||||||
getNextIDSuffix: getNextIDSuffix,
|
|
||||||
enhanceItemWithID: enhanceItemWithID,
|
|
||||||
extend: extend,
|
|
||||||
pop: pop
|
|
||||||
}
|
}
|
||||||
})
|
var firstLetter = str.substr(0, 1).toUpperCase();
|
||||||
|
return firstLetter + str.substr(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNextIDSuffix(container, regexp) {
|
||||||
|
var max = Math.max.apply(Math, container.getIDs().map(function(id) {
|
||||||
|
var match = regexp.exec(id);
|
||||||
|
return match && +match[2];
|
||||||
|
}));
|
||||||
|
return max > 0 ? max + 1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function enhanceItemWithID(item, id) {
|
||||||
|
item.setID(id);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pop(obj, key) {
|
||||||
|
if ( obj.hasOwnProperty(key) ) {
|
||||||
|
var value = obj[key];
|
||||||
|
delete obj[key];
|
||||||
|
return value;
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function extend(proto, extension) {
|
||||||
|
var newObj;
|
||||||
|
proto = (angular.isDefined(proto) ? proto : null);
|
||||||
|
newObj = Object.create(proto);
|
||||||
|
Object.keys(extension).forEach(function(key) {
|
||||||
|
newObj[key] = extension[key];
|
||||||
|
});
|
||||||
|
return newObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
getMeta: getMeta,
|
||||||
|
getNewId: getNewId,
|
||||||
|
groupByMetaKey: groupByMetaKey,
|
||||||
|
makeTitle: makeTitle,
|
||||||
|
getNextIDSuffix: getNextIDSuffix,
|
||||||
|
enhanceItemWithID: enhanceItemWithID,
|
||||||
|
extend: extend,
|
||||||
|
pop: pop,
|
||||||
|
condense: condense
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -16,15 +16,10 @@ describe('merlin.utils', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('condense Array method', function() {
|
describe('condense function', function() {
|
||||||
it('Array prototype should have condense()', function() {
|
|
||||||
var array = [];
|
|
||||||
expect(array.condense).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('condense() should throw away undefined and null values', function() {
|
it('condense() should throw away undefined and null values', function() {
|
||||||
var array = [1, 0, 15, undefined, 7, null, null, 8];
|
var array = [1, 0, 15, undefined, 7, null, null, 8];
|
||||||
expect(array.condense()).toEqual([1, 0, 15, 7, 8]);
|
expect(utils.condense(array)).toEqual([1, 0, 15, 7, 8]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue