vormjs
Version:
Write your forms in JSON and HTML, use the same API.
1,570 lines (1,203 loc) • 39.6 kB
JavaScript
;
var _slicedToArray = function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } };
/*global require*/
(function () {
/**
* @ngdoc module
* @name vorm
* @module vorm
* @description
* The `vorm` module is the core and currently only module for vorm.
*
*/
angular.module('vorm', []);
})();
/*global angular*/
(function () {
angular.module('vorm').factory('VormEvent', ['$document', '$window', function ($document, $window) {
var VormEvent = undefined;
try {
var _event = new $window.CustomEvent('foo'); // jshint ignore:line
VormEvent = function (type, data) {
return new $window.CustomEvent(type, {
detail: data,
bubbles: true
});
};
} catch (error) {
VormEvent = function (type, data) {
var event = $document[0].createEvent('CustomEvent');
event.initCustomEvent(type, true, true, data);
return event;
};
}
return VormEvent;
}]);
})();
/*global angular,_*/
(function () {
angular.module('vorm').factory('VormFieldCtrl', ['$rootScope', 'VormEvent', 'VormValueType', 'VormValueScope', function ($rootScope, VormEvent, VormValueType, VormValueScope) {
return function (name, element) {
var ctrl = {},
models = [],
viewChangeListeners = [],
modelChangeListeners = [],
prefix = 'vorm-field-';
var valueType = VormValueType.SINGLE,
required = undefined,
valueScope = new VormValueScope();
function getDefaultValue() {
var defaults = undefined;
switch (valueType) {
case VormValueType.SINGLE:
defaults = null;
break;
case VormValueType.LIST:
defaults = [null];
break;
case VormValueType.NAMED:
defaults = {};
break;
}
return defaults;
}
function setModelValue(model, value) {
// $$writeModelToScope calls the view listeners
// and we don't really want that
var $viewChangeListeners = model.$viewChangeListeners;
var $modelValue = model.$modelValue;
model.$viewChangeListeners = [];
model.$modelValue = value;
model.$$writeModelToScope();
// reset all the things
model.listeners = $viewChangeListeners;
model.$modelValue = $modelValue;
}
function applyValueToControls() {
var value = valueScope[name];
switch (valueType) {
case VormValueType.SINGLE:
if (models[0]) {
setModelValue(models[0], value);
}
break;
case VormValueType.LIST:
_.each(models, function (model, index) {
setModelValue(model, value[index]);
});
break;
case VormValueType.NAMED:
var modelsToChange = models.concat();
_.each(value, function (val, key) {
var model = _.find(models, { $name: key });
if (model) {
setModelValue(model, val);
}
_.pull(modelsToChange, model);
});
_.each(modelsToChange, function (model) {
setModelValue(model, undefined);
});
break;
}
}
function getModelValue() {
var value = undefined;
switch (valueType) {
case VormValueType.SINGLE:
value = models[0] ? models[0].$modelValue : undefined;
break;
case VormValueType.LIST:
value = _.pluck(models, '$modelValue');
break;
case VormValueType.NAMED:
value = {};
_.each(models, function (model) {
value[model.$name] = model.$modelValue;
});
break;
}
return value;
}
function handleViewChange() {
// value changes from view
valueScope[name] = getModelValue();
ctrl.triggerViewChange();
}
function handleFormatterCall(value) {
// value changes from model
var modelValue = getModelValue();
valueScope[name] = modelValue;
ctrl.triggerModelChange();
return value;
}
ctrl.triggerModelChange = function () {
element.dispatchEvent(new VormEvent('modelchange', { name: name }));
_.invoke(modelChangeListeners, 'call', null, name);
};
ctrl.triggerViewChange = function () {
element.dispatchEvent(new VormEvent('viewchange', { name: name }));
_.invoke(viewChangeListeners, 'call', null, name);
};
ctrl.getName = function () {
return name;
};
ctrl.setName = function () {
name = arguments[0];
};
ctrl.addModel = function (model) {
models.push(model);
model.$viewChangeListeners.push(handleViewChange);
model.$formatters.push(handleFormatterCall);
valueScope[name] = getModelValue();
};
ctrl.removeModel = function (model) {
_.pull(models, model);
_.pull(model.$viewChangeListeners, handleViewChange);
_.pull(model.$formatters, handleFormatterCall);
valueScope[name] = getModelValue();
};
ctrl.getModels = function () {
return models;
};
ctrl.getValue = function () {
return valueScope[name];
};
ctrl.getValueType = function () {
return valueType;
};
ctrl.setValueType = function (type) {
if ([VormValueType.SINGLE, VormValueType.LIST, VormValueType.NAMED].indexOf(type) === -1) {
throw new Error('Unsupported VormValueType: ' + VormValueType);
}
valueType = type;
valueScope[name] = getDefaultValue();
};
ctrl.setValue = function (value) {
valueScope[name] = value;
applyValueToControls();
};
ctrl.setEmpty = function () {
valueScope[name] = getDefaultValue();
};
ctrl.isRequired = function () {
return required;
};
ctrl.setRequired = function (r) {
required = !!r;
};
ctrl.isEmpty = function () {
return models.every(function (model) {
return model.$isEmpty(model.$viewValue);
});
};
ctrl.setValueScope = function (scope) {
var val = valueScope[name];
if (valueScope) {
valueScope.$destroy();
}
valueScope = scope;
valueScope[name] = val;
};
ctrl.getValueScope = function () {
return valueScope;
};
var chain = _('valid invalid dirty pristine touched untouched required empty'.split(' ')).map(function (key) {
return prefix + key;
}).zipObject().mapValues(function (value, key) {
var m = key.substr(prefix.length);
return ctrl['is' + _.capitalize(m)]();
});
ctrl.getClassObj = function () {
return chain.value();
};
ctrl.setEmpty();
ctrl.viewChangeListeners = viewChangeListeners;
ctrl.modelChangeListeners = modelChangeListeners;
'valid invalid dirty pristine touched untouched'.split(' ').forEach(function (type) {
var capitalized = _.capitalize(type),
getName = 'is' + capitalized,
propertyName = '$' + type,
setName = 'set' + capitalized,
method = ['valid', 'pristine', 'untouched'].indexOf(type) !== -1 ? 'every' : 'some';
ctrl[getName] = function () {
return models[method](function (model) {
return model[propertyName];
});
};
if (type !== 'valid' && type !== 'invalid') {
ctrl[setName] = function () {
var outerArgs = arguments;
models.forEach(function (model) {
model['$' + setName].apply(model, outerArgs);
});
};
}
});
valueScope[name] = getDefaultValue();
return ctrl;
};
}]);
})();
/*global angular*/
(function () {
angular.module('vorm').factory('VormModelDelegate', [function () {
return function (name) {
var delegate = {};
var ngModel = null;
delegate.value = null;
delegate.getName = function () {
return name;
};
delegate.setNgModel = function (model) {
ngModel = model;
if (ngModel) {
ngModel.$name = name;
}
};
delegate.unsetNgModel = function () {
ngModel = null;
};
delegate.getNgModel = function () {
return ngModel;
};
delegate.clearViewValue = function () {
ngModel.$setViewValue(null);
ngModel.$render();
};
delegate.getViewValue = function () {
return ngModel ? ngModel.$viewValue : delegate.value;
};
return delegate;
};
}]);
})();
/*global angular*/
(function () {
angular.module('vorm').factory('VormValueScope', ['$rootScope', function ($rootScope) {
return function () {
var scope = $rootScope.$new();
return scope;
};
}]);
})();
/*global angular*/
(function () {
angular.module('vorm').constant('VormValueType', {
SINGLE: 'single',
LIST: 'list',
NAMED: 'named'
});
})();
/*global angular*/
(function () {
/**
* @ngdoc directive
* @name ngModel
* @module vorm
* @description
* This overloads the `ngModel` directive, and registers the `ngModelController`
* with the `vormFieldController` and the `vormFormController` if they're there.
* __Requires__: `ngModel`, `^?vormField`, `^?vormForm`
*/
angular.module('vorm').directive('ngModel', ['VormFieldCtrl', function (VormFieldCtrl) {
return {
require: ['ngModel', '^?vormField', '^?vormForm'],
compile: function compile() {
return function link(scope, element, attrs, controllers) {
var _controllers = _slicedToArray(controllers, 3);
var ngModel = _controllers[0];
var vormField = _controllers[1];
var vorm = _controllers[2];
if (vormField || vorm) {
if (!vormField) {
vormField = new VormFieldCtrl(attrs.name || attrs.ngModel, element[0]);
if (vorm) {
vorm.addField(vormField);
scope.$on('$destroy', function () {
vorm.removeField(vormField);
});
}
}
vormField.addModel(ngModel);
scope.$on('$destroy', function () {
vormField.removeModel(ngModel);
});
}
};
}
};
}]);
})();
/*global angular*/
(function () {
/**
* @ngdoc directive
* @name vormChange
* @module vorm
* @description
Evaluate the given expression when a value changes from the view.
It listens to a viewchange event, which is dispatched from a
`vormFieldController` and then bubbles upwards.
*/
angular.module('vorm').directive('vormChange', ['$parse', function ($parse) {
return {
link: function link(scope, element, attrs) {
var cb = $parse(attrs.vormChange);
function handleChange(event, name) {
cb(scope, {
$event: event,
$name: event.detail ? event.detail.name : name
});
}
element.bind('viewchange', handleChange);
}
};
}]);
})();
/*global angular,_*/
(function () {
angular.module('vorm').directive('vormControl', ['$document', function ($document) {
var matchesFuncName = (function () {
var element = $document[0].createElement('div');
return _(['', 'ms', 'moz', 'webkit']).map(function (prefix) {
return prefix ? prefix + 'MatchesSelector' : 'matches';
}).find(function (name) {
return name in element;
});
})();
return {
restrict: 'E',
require: ['vormControl', '^vormField', '^vormFieldConfig', '^vormFocusableList', '^vormFieldTemplate'],
controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
var ctrl = this,
vormField,
vormFieldConfig,
vormFocusableList,
vormFieldTemplate,
inputId = Math.random().toString(36).slice(2);
ctrl.link = function (controllers) {
vormField = controllers[0];
vormFieldConfig = controllers[1];
vormFocusableList = controllers[2];
vormFieldTemplate = controllers[3];
ctrl.invokeData = vormFieldConfig.invokeData;
ctrl.getData = vormFieldConfig.getData;
ctrl.getConfig = vormFieldConfig.getConfig;
vormFocusableList.addId(inputId);
vormFieldTemplate.getModelCompiler()($scope, function (clonedElement) {
var focusable = undefined,
selector = 'input,keygen,meter,output,progress,select,textarea',
replace = $element.find('vorm-control-replace'),
delegate = $scope.$eval($attrs.delegate);
replace.replaceWith(clonedElement);
clonedElement[0].className += ' ' + replace[0].className;
if (clonedElement[0][matchesFuncName](selector)) {
focusable = clonedElement;
} else {
focusable = angular.element(clonedElement[0].querySelector(selector));
}
focusable.attr('id', ctrl.getInputId());
$scope.$$postDigest(function () {
delegate.setNgModel(clonedElement.controller('ngModel'));
});
$scope.$on('$destroy', function () {
delegate.unsetNgModel();
});
});
};
ctrl.isRequired = function () {
return vormField && vormField.isRequired();
};
ctrl.getInputId = function () {
return inputId;
};
ctrl.getViewValue = function () {
return $scope.$eval($attrs.delegate).getViewValue();
};
ctrl.getDisplayMode = function () {
return vormFieldConfig.getDisplayMode();
};
if (angular.version.minor >= 4) {
// dynamic options throws an error in <=1.3.x
// fixed in 1.4.x
// https://github.com/angular/angular.js/pull/10639
ctrl.getOptions = function () {
return ctrl.invokeData('options');
};
} else {
ctrl.getOptions = (function () {
var options = undefined;
return function () {
var nwOpts = ctrl.invokeData('options');
if (options !== nwOpts && !angular.equals(options, nwOpts)) {
options = nwOpts;
}
return options;
};
})();
}
$scope.$on('$destroy', function () {
vormFocusableList.removeId(inputId);
});
}],
controllerAs: 'vormControl',
link: function link(scope, element, attrs, controllers) {
controllers[0].link(controllers.slice(1));
}
};
}]);
})();
/*global angular,_*/
(function () {
/**
@ngdoc directive
@name vormControlList
@module vorm
@description
This directives manages and displays the available controls.
__Requires__: `^vormFieldConfig`, `^?vormFocusableList`, `vormField`
*/
/**
@ngdoc type
@name vormControlList.controller
@module vorm
*/
angular.module('vorm').directive('vormControlList', ['VormModelDelegate', '$document', function (VormModelDelegate, $document) {
return {
require: ['vormControlList', '^vormFieldConfig', '^?vormFocusableList', '^vormField'],
restrict: 'E',
controller: ['$scope', function ($scope) {
var ctrl = this,
delegates = [],
limit = NaN,
vormFieldConfig = undefined,
vormFocusableList = undefined,
vormField = undefined;
function triggerAsyncViewChange(callback) {
var unwatch = $scope.$watchCollection(vormField.getModels, function () {
vormField.triggerViewChange();
callback();
unwatch();
});
}
function setFocus() {
if (vormFocusableList) {
var id = vormFocusableList.getId(),
el = $document[0].getElementById(id);
if (el) {
el.focus();
}
}
}
function createDelegate(name) {
var delegate = undefined,
value = undefined;
delegate = new VormModelDelegate(name);
switch (vormField.getValueType()) {
case 'list':
value = vormField.getValue()[delegates.length];
break;
case 'named':
value = vormField.getValue()[name];
break;
case 'single':
value = vormField.getValue();
break;
}
delegate.value = value;
delegates.push(delegate);
}
ctrl.link = function (controllers) {
vormFieldConfig = controllers[0];
vormFocusableList = controllers[1];
vormField = controllers[2];
$scope.$watch(vormFieldConfig.getLimit, function (limit) {
ctrl.setLimit(limit);
});
$scope.$watchCollection(function () {
var keys = undefined,
val = vormField.getValue();
switch (vormField.getValueType()) {
default:
keys = _.keys(val);
break;
case 'single':
keys = null;
break;
}
return keys;
}, function (keys) {
delegates = [];
if (!keys) {
createDelegate();
}
_.each(keys, function (key) {
createDelegate(key);
});
});
};
/**
* @ngdoc method
* @name vormControlList.controller#$getDelegates
*
* @description
Returns the list of the model delegates that are registered with the controller.
* @returns {Array.<VormModelDelegate>} A list of the registered model delegates.
*/
ctrl.getDelegates = function () {
return delegates;
};
ctrl.clearDelegate = function (delegate) {
delegate.clearViewValue();
};
ctrl.getLimit = function () {
return limit;
};
ctrl.setLimit = function (l) {
limit = Number(l);
};
ctrl.reachedLimit = function () {
return limit > 0 && delegates.length >= limit;
};
ctrl.isClearButtonVisible = function () {
return vormField.getValueType() === 'list';
};
ctrl.handleCreateClick = function () {
vormField.setValue(vormField.getValue().concat(null));
triggerAsyncViewChange(setFocus);
};
ctrl.handleClearClick = function (delegate) {
if (delegates.length === 1) {
delegate.clearViewValue();
} else {
var value = vormField.getValue(),
index = _.find(vormField.getModels(), { model: delegate.getNgModel() });
value.splice(index, 1);
vormField.setValue(value);
triggerAsyncViewChange(setFocus);
}
};
}],
controllerAs: 'vormControlList',
link: function link(scope, element, attrs, controllers) {
controllers.shift().link(controllers);
}
};
}]);
})();
/*global angular*/
(function () {
angular.module('vorm').directive('vormDelegateButton', [function () {
return {
require: ['vormDelegateButton', '^vormFieldConfig', '^vormControlList', '^vormField'],
template: '<button class="vorm-delegate-button" type="button" ng-click="vormDelegateButton.handleClick()" ng-disabled="vormDelegateButton.isDisabled()" ng-show="vormDelegateButton.isVisible()">' + '{{vormDelegateButton.getLabel()}}' + '</button>',
replace: true,
controller: [function () {
var ctrl = this,
vormFieldConfig = undefined,
vormControlList = undefined,
vormField = undefined;
ctrl.link = function (controllers) {
vormFieldConfig = controllers[0];
vormControlList = controllers[1];
vormField = controllers[2];
};
ctrl.handleClick = function () {
vormControlList.handleCreateClick();
};
ctrl.isDisabled = function () {
return vormControlList.reachedLimit();
};
ctrl.isVisible = function () {
return vormField.getValueType() === 'list';
};
ctrl.getLabel = function () {
var config = vormFieldConfig.getConfig(),
typeOptions = config ? config.valueType : null,
addLabel = typeOptions && typeOptions.addLabel ? vormFieldConfig.invoke(typeOptions.addLabel) : '';
return addLabel;
};
}],
link: function link(scope, element, attrs, controllers) {
controllers.shift().link(controllers);
},
controllerAs: 'vormDelegateButton'
};
}]);
})();
/*global angular*/
(function () {
angular.module('vorm').directive('vormDisplay', ['vormTemplateService', function (vormTemplateService) {
return {
restrict: 'E',
require: ['vormDisplay', '^vormControl', '^vormFieldConfig'],
controller: ['$scope', '$element', function ($scope, $element) {
var ctrl = this,
vormControl = undefined,
vormFieldConfig = undefined;
ctrl.link = function (controllers) {
var template = undefined,
compiler = undefined,
config = undefined;
vormControl = controllers[0];
vormFieldConfig = controllers[1];
config = vormFieldConfig.getConfig();
template = config.template ? config.template.display : null;
compiler = vormTemplateService.getDisplayCompiler(config.type, template);
compiler($scope, function (clonedElement) {
$element.append(clonedElement);
});
ctrl.getViewValue = vormControl.getViewValue;
ctrl.getModelValue = vormControl.getModelValue;
};
}],
controllerAs: 'vormDisplay',
link: function link(scope, element, attrs, controllers) {
controllers.shift().link(controllers);
}
};
}]);
})();
/*global angular*/
(function () {
angular.module('vorm').directive('vormField', ['VormFieldCtrl', function (VormFieldCtrl) {
return {
scope: true,
require: ['vormField', '^?vormForm'],
controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
var name = $scope.$eval($attrs.vormField) || $attrs.name || $attrs.ngModel,
ctrl = this;
angular.extend(ctrl, new VormFieldCtrl(name, $element[0]));
ctrl.link = function (controllers) {
var _controllers2 = _slicedToArray(controllers, 1);
var vorm = _controllers2[0];
if (vorm) {
vorm.addField(ctrl);
$scope.$on('$destroy', function () {
vorm.removeField(ctrl);
});
}
};
}],
controllerAs: 'vormField',
link: function link(scope, element, attrs, controllers) {
controllers.shift().link(controllers);
}
};
}]);
})();
/*global angular,_*/
(function () {
angular.module('vorm').directive('vormFieldConfig', ['vormInvoke', function (vormInvoke) {
return {
require: ['vormFieldConfig', 'vormField', '^?vormForm'],
controller: ['$scope', '$attrs', function ($scope, $attrs) {
var ctrl = this,
config = $scope.$eval($attrs.vormFieldConfig),
vormField = undefined,
vormForm = undefined;
function getValues() {
var values = undefined;
if (vormForm) {
values = vormForm.getValues();
} else if (vormField) {
values = {};
values[vormField.getName()] = vormField.getValue();
}
return values;
}
ctrl.link = function (controllers) {
vormField = controllers[0];
vormForm = controllers[1];
vormField.setName(config.name);
if (config.valueType !== undefined) {
if (typeof config.valueType === 'string') {
vormField.setValueType(config.valueType);
} else if (config.valueType.type !== undefined) {
vormField.setValueType(config.valueType.type);
}
}
if (_.isArray(config.required) || typeof config.required === 'function') {
$scope.$watch(function () {
return ctrl.invoke(config.required);
}, function (isRequired) {
vormField.setRequired(!!isRequired);
});
} else {
vormField.setRequired(config.required || false);
}
if (config.defaults) {
vormField.setValue(ctrl.invoke(config.defaults));
}
};
ctrl.invoke = function (invokable) {
return vormInvoke(invokable, {
$values: getValues()
});
};
ctrl.invokeExpr = function (invokable) {
return vormInvoke.expr(invokable, {
$values: getValues()
}, vormField.getValueScope());
};
ctrl.getConfig = function () {
return config;
};
ctrl.invokeData = function (key) {
return ctrl.invoke(config.data[key]);
};
ctrl.getLimit = function () {
var limit = 1;
if (vormField.getValueType() === 'list') {
limit = -1;
if (config.valueType && config.valueType.limit !== undefined) {
limit = ctrl.invoke(config.valueType.limit);
}
}
return limit;
};
ctrl.getDisplayMode = function () {
return ctrl.invokeExpr(config.disabled) ? 'display' : 'edit';
};
}],
controllerAs: 'vormFieldConfig',
link: function link(scope, element, attrs, controllers) {
controllers.shift().link(controllers);
}
};
}]);
})();
/*global angular,_*/
(function () {
angular.module('vorm').directive('vormFieldTemplate', ['vormTemplateService', function (vormTemplateService) {
var wrapperEl = angular.element(vormTemplateService.getDefaultTemplate());
angular.element(wrapperEl[0].querySelectorAll('vorm-replace')).replaceWith(vormTemplateService.getDefaultControlTemplate());
wrapperEl.attr('vorm-field-config', 'vormFieldTemplate.getConfig()');
wrapperEl.attr('vorm-focusable-list', '');
var template = wrapperEl[0].outerHTML;
return {
scope: true,
restrict: 'E',
template: template,
replace: true,
controller: ['$scope', '$attrs', '$element', function ($scope, $attrs, $element) {
var ctrl = this,
config = $scope.$eval($attrs.config) || {},
compiler = undefined;
config = _.defaults(angular.copy(config), {
name: $attrs.name,
type: $attrs.type,
label: $attrs.label,
template: $scope.$eval($attrs.template),
required: $scope.$eval($attrs.required),
data: $scope.$eval($attrs.data) || {}
});
if (!config.name || !config.type) {
throw new Error('Missing one of required arguments: name, type ');
}
compiler = vormTemplateService.getModelCompiler(config.type, config.modelTemplate);
$element.attr('vorm-field', config.name);
ctrl.getConfig = function () {
return config;
};
ctrl.getModelCompiler = function () {
return compiler;
};
}],
controllerAs: 'vormFieldTemplate'
};
}]);
})();
/*global angular*/
(function () {
angular.module('vorm').directive('vormFieldWrapper', ['vormTemplateService', function (vormTemplateService) {
var wrapped = angular.element(vormTemplateService.getDefaultTemplate());
wrapped.find('vorm-replace').append('<ng-transclude></ng-transclude>');
wrapped.attr('vorm-field-config', 'vormFieldWrapper.getConfig()');
wrapped.attr('vorm-focusable-list', '');
var template = wrapped[0].outerHTML;
return {
restrict: 'A',
transclude: true,
template: template,
replace: true,
controller: ['$attrs', function ($attrs) {
var ctrl = this,
config = {
name: $attrs.name,
label: $attrs.label
};
ctrl.getConfig = function () {
return config;
};
}],
controllerAs: 'vormFieldWrapper'
};
}]);
})();
/*global angular*/
(function () {
angular.module('vorm').directive('vormFieldset', ['vormInvoke', function (vormInvoke) {
return {
restrict: 'E',
require: ['vormFieldset', '^?vormForm'],
template: '\n\t\t\t\t\t<fieldset>\n\t\t\t\t\t\t<vorm-field-template config="field" ng-repeat="field in vormFieldset.getFields() | filter:vormFieldset.isVisible:field">\n\t\t\t\t\t\t</vorm-field-template>\n\t\t\t\t\t</fieldset>\n\t\t\t\t',
replace: true,
controller: ['$scope', '$attrs', function ($scope, $attrs) {
var ctrl = this,
vormForm = undefined,
valueScope = undefined;
function getValues() {
var vals = {};
if (vormForm) {
vals = vormForm.getValues();
}
return vals;
}
ctrl.link = function (controllers) {
vormForm = controllers[0];
if (vormForm) {
valueScope = vormForm.getValueScope();
}
};
ctrl.getFields = function () {
return $scope.$eval($attrs.fields);
};
ctrl.isVisible = function (field) {
return field.when === null || field.when === undefined ? true : !!vormInvoke.expr(field.when, { $values: getValues() }, valueScope);
};
}],
controllerAs: 'vormFieldset',
link: function link(scope, element, attrs, controllers) {
controllers.shift().link(controllers);
}
};
}]);
})();
/*global angular,_*/
(function () {
angular.module('vorm').directive('vormFocusableList', [function () {
return {
controller: [function () {
var ctrl = this,
ids = [];
ctrl.addId = function (id) {
ids.push(id);
};
ctrl.removeId = function (id) {
_.pull(ids, id);
};
ctrl.getId = function () {
return ids[ids.length - 1];
};
}]
};
}]);
})();
/*global angular,_*/
(function () {
angular.module('vorm').directive('vormForm', ['VormValueScope', function (VormValueScope) {
return {
scope: true,
require: ['form'],
controller: ['$element', function ($element) {
var ctrl = this,
fields = [],
changeListeners = [],
submitListeners = [],
valueScope = new VormValueScope();
function handleChange() {
var outerArgs = arguments;
_.each(changeListeners, function (listener) {
listener.apply(ctrl, outerArgs);
});
}
function getFieldByName(name) {
return _.find(fields, function (field) {
return field.getName() === name;
});
}
ctrl.addField = function (field) {
fields.push(field);
field.viewChangeListeners.push(handleChange);
field.setValueScope(valueScope);
};
ctrl.removeField = function (field) {
_.pull(fields, field);
_.pull(field.viewChangeListeners, handleChange);
};
ctrl.getFields = function () {
return fields;
};
ctrl.getValues = function () {
var values = _(fields).indexBy(function (field) {
return field.getName();
}).mapValues(function (field) {
return field.getValue();
}).value();
return values;
};
ctrl.getValue = function (name) {
return getFieldByName(name).getValue();
};
ctrl.setValue = function (name, value) {
getFieldByName(name).setValue(value);
};
ctrl.getValueScope = function () {
return valueScope;
};
ctrl.changeListeners = changeListeners;
ctrl.submitListeners = submitListeners;
'valid invalid dirty pristine touched untouched'.split(' ').forEach(function (type) {
var capitalized = type.substr(0, 1).toUpperCase() + type.substr(1),
getName = 'is' + capitalized,
setName = 'set' + capitalized,
method = ['valid', 'pristine', 'untouched'].indexOf(type) !== -1 ? 'every' : 'some';
ctrl[getName] = function () {
return fields[method](function (field) {
return field[getName]();
});
};
if (!(type === 'valid' || type === 'invalid')) {
ctrl[setName] = function () {
var outerArgs = arguments;
fields.forEach(function (field) {
field[setName].apply(field, outerArgs);
});
};
}
});
$element.bind('submit', function () {
_.invoke(submitListeners, 'call', null, ctrl.getValues());
});
return ctrl;
}],
controllerAs: 'vormForm'
};
}]);
})();
/*global angular,_*/
(function () {
angular.module('vorm').factory('vormInvoke', ['$injector', '$parse', function ($injector, $parse) {
function invoke(invokable, locals) {
var value = undefined;
if (!invokable) {
return invokable;
}
if (_.isArray(invokable) && typeof _.last(invokable) === 'function' || invokable.$inject !== undefined) {
value = $injector.invoke(invokable, null, locals ? angular.copy(locals) : null);
} else if (typeof invokable === 'function') {
value = invokable();
} else {
value = invokable;
}
return value;
}
var invoker = function invoker(invokable, locals) {
return invoke(invokable, locals);
};
invoker.expr = function (invokable, locals, scope) {
var value = undefined;
if (typeof invokable === 'string') {
value = $parse(invokable)(scope, locals);
} else {
value = invoke(invokable, locals);
}
return value;
};
return invoker;
}]);
})();
/*global angular*/
(function () {
angular.module('vorm').directive('vormLabel', [function () {
return {
require: ['vormLabel', '^vormFieldConfig', '^vormFocusableList'],
template: '<label class="vorm-field-label">{{vormLabel.getLabel()}}</label>',
replace: true,
controller: ['$scope', '$element', function ($scope, $element) {
var ctrl = this,
vormFieldConfig = undefined,
vormFocusableList = undefined;
ctrl.link = function (controllers) {
vormFieldConfig = controllers[0];
vormFocusableList = controllers[1];
$scope.$watch(vormFocusableList.getId, function (inputId) {
$element.attr('for', inputId);
});
};
ctrl.getLabel = function () {
return vormFieldConfig.invoke(vormFieldConfig.getConfig().label);
};
}],
controllerAs: 'vormLabel',
link: function link(scope, element, attrs, controllers) {
controllers[0].link(controllers.slice(1));
}
};
}]);
})();
/*global angular*/
(function () {
angular.module('vorm').directive('vormSubmit', ['$parse', function ($parse) {
return {
require: ['vormForm'],
link: function link(scope, element, attrs, controllers) {
var _controllers3 = _slicedToArray(controllers, 1);
var vorm = _controllers3[0];
var cb = undefined;
function handleSubmit() {
cb(scope, {
$values: vorm.getValues()
});
}
cb = $parse(attrs.vormSubmit);
element.bind('submit', handleSubmit);
}
};
}]);
})();
/*global angular,_*/
(function () {
angular.module('vorm').provider('vormTemplateService', [function () {
var wrapperTemplate = undefined,
controlTemplate = undefined,
compilers = { model: {}, display: {} },
templates = { model: {}, display: { 'null': angular.element('<span>{{vormDisplay.getViewValue()}}</span>') } };
var vormTemplateService = {};
wrapperTemplate = '<div ng-class="vormField.getClassObj()">' + '<vorm-label></vorm-label>' + '<vorm-replace></vorm-replace>' + '</div>';
controlTemplate = '<vorm-control-list>' + '<vorm-control ng-repeat="delegate in vormControlList.getDelegates()" delegate="delegate">' + '<vorm-edit ng-show="vormControl.getDisplayMode()===\'edit\'">' + '<vorm-control-replace></vorm-control-replace>' + '<button class="vorm-control-clear-button" type="button" ng-click="vormControlList.handleClearClick(delegate)" ng-show="vormControlList.isClearButtonVisible()">x</button>' + '</vorm-edit>' + '<vorm-display ng-show="vormControl.getDisplayMode()===\'display\'"></vorm-display>' + '</vorm-control>' + '<vorm-delegate-button>' + '</vorm-delegate-button>' + '</vorm-control-list>';
function modifyModelTemplates(processor) {
templates.model = _.mapValues(templates.model, function (template, type) {
return processor(template, type);
});
}
function modifyDisplayTemplates(processor) {
templates.display = _.mapValues(templates.display, function (template, type) {
return processor(template, type);
});
}
function modifyTemplate(processor) {
var processedEl = processor(angular.element(wrapperTemplate));
processedEl.attr('vorm-field', '');
processedEl.find('vorm-control').attr('limit', 'vormFieldConfig.getLimit()');
wrapperTemplate = processedEl[0].outerHTML;
}
function modifyControlTemplate(processor) {
var wrapper = angular.element('<p></p>');
wrapper.append(processor(angular.element(controlTemplate)));
controlTemplate = wrapper[0].innerHTML;
}
function registerType(type, modelTemplate, displayTemplate) {
templates.model[type] = modelTemplate;
if (displayTemplate) {
templates.display[type] = displayTemplate;
}
}
modifyTemplate(function () {
return angular.element(wrapperTemplate);
});
modifyControlTemplate(function () {
return angular.element(controlTemplate);
});
return {
$get: ['$compile', function ($compile) {
function getCompiler(type, controlType, template) {
var compiler = undefined,
pool = compilers[type];
if (template) {
compiler = $compile(template);
} else {
compiler = pool[controlType];
}
if (!compiler && type === 'display') {
compiler = getCompiler(type, null);
}
if (!compiler) {
throw new Error('' + _.capitalize(type) + ' template for ' + controlType + ' not found');
}
return compiler;
}
vormTemplateService.getDefaultTemplate = function () {
return wrapperTemplate;
};
vormTemplateService.getDefaultControlTemplate = function () {
return controlTemplate;
};
vormTemplateService.getModelCompiler = function (type, template) {
return getCompiler('model', type, template);
};
vormTemplateService.getDisplayCompiler = function (type, template) {
return getCompiler('display', type, template);
};
compilers.model = _.mapValues(templates.model, function (el) {
var modelEl = undefined;
_.some(el, function (element) {
var childEl = undefined;
if (element.hasAttribute('ng-model')) {
modelEl = angular.element(element);
} else if (childEl = element.querySelector('[ng-model]')) {
modelEl = angular.element(childEl);
}
return !!modelEl;
});
if (!modelEl) {
modelEl = el;
}
modelEl.attr('ng-model', 'delegate.value');
modelEl.attr('name', '{{delegate.getName()}}');
modelEl.attr('ng-required', 'vormControl.isRequired()');
return $compile(el);
});
compilers.display = _.mapValues(templates.display, function (el) {
return $compile(el);
});
return vormTemplateService;
}],
modifyModelTemplates: modifyModelTemplates,
modifyDisplayTemplates: modifyDisplayTemplates,
modifyControlTemplate: modifyControlTemplate,
modifyTemplate: modifyTemplate,
registerType: registerType
};
}]);
})();
/*global angular,_*/
(function () {
angular.module('vorm').config(['vormTemplateServiceProvider', function (vormTemplateServiceProvider) {
var templates = _('date datetime datetime-local email month number password search tel text time url week checkbox'.split(' ')).zipObject().mapValues(function (value, key) {
var placeholder = _.includes('text search tel url email number password'.split(' '), key) ? 'placeholder="{{vormControl.invokeData(\'placeholder\')}}"' : '',
tpl = '<input type="' + key + '" ' + placeholder + ' ng-model/>';
if (key === 'checkbox') {
tpl = '<label for="{{vormControl.getInputId()}}">' + tpl + '{{vormControl.invokeData("checkboxLabel")}}' + '</label>';
}
return tpl;
}).value();
for (var type in templates) {
vormTemplateServiceProvider.registerType(type, angular.element(templates[type]));
}
}]);
})();
/*global angular*/
(function () {
angular.module('vorm').config(['vormTemplateServiceProvider', function (vormTemplateServiceProvider) {
var el = angular.element('<div class="vorm-radio-group">' + '<label ng-repeat="option in vormControl.getOptions()">' + '<input type="radio" ng-model name="{{::vormField.getName()}}" value="{{::option.value}}"/>' + '{{::option.label}}' + '</label>' + '</div>');
vormTemplateServiceProvider.registerType('radio', el);
}]);
})();
/*global angular*/
(function () {
angular.module('vorm').config(['vormTemplateServiceProvider', function (vormTemplateServiceProvider) {
var el = angular.element('<select ng-options="option.value as option.label for option in vormControl.getOptions()"><option value="" ng-show="!!vormControl.invokeData(\'notSelectedLabel\')">{{vormControl.invokeData(\'notSelectedLabel\')}}</option></select>');
vormTemplateServiceProvider.registerType('select', el);
}]);
})();
/*global angular*/
(function () {
angular.module('vorm').config(['vormTemplateServiceProvider', function (vormTemplateServiceProvider) {
var el = angular.element('<textarea placeholder="{{vormControl.invokeData(\'placeholder\')}}"></textarea>');
vormTemplateServiceProvider.registerType('textarea', el);
}]);
})();
//# sourceMappingURL=vorm.js.map