Last active
August 29, 2015 14:01
-
-
Save PawelGerr/16e6965e866dc8a0b7fe to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function () { | |
'use strict'; | |
/** | |
* @param $scope | |
* @param $parse | |
*/ | |
function TtValidationController($scope, $parse) { | |
var context = null; | |
var controls = []; | |
/** | |
* @param {TtValidateController} control | |
*/ | |
this.addValidationControl = function (control) { | |
if (!control) | |
throw new Error('Validation control must not be null.'); | |
controls.push(control); | |
if (context) | |
initControl(control, context); | |
}; | |
/** | |
* @param {TtValidateController} control | |
*/ | |
this.removeValidationControl = function (control) { | |
removeFromArray(controls, control); | |
}; | |
/** | |
* @param controlElement | |
*/ | |
this.getNgRepeats = function (controlElement, ngModelExpression) { | |
var ngRepeats = []; | |
var previousVariable = getFirstVariableName(ngModelExpression); | |
while (controlElement && (controlElement.length > 0) && !$scope.isValidationElement(controlElement)) { | |
var ngRepeatExp = getNgRepeatExpression(controlElement); | |
if (ngRepeatExp) { | |
var parsedExpression = parseNgRepeat(ngRepeatExp); | |
if (parsedExpression.valueExp === previousVariable) { | |
previousVariable = getFirstVariableName(parsedExpression.collectionExp); | |
ngRepeats.push(parsedExpression); | |
} | |
} | |
controlElement = controlElement.parent(); | |
} | |
return ngRepeats; | |
}; | |
$scope.init = function (validationContext) { | |
if (context) { | |
throw new Error('Validation is initialized already.'); | |
} | |
context = validationContext; | |
for (var i = 0; i < controls.length; i++) { | |
var control = controls[i]; | |
initControl(control); | |
} | |
}; | |
/** | |
* @param {TtValidateController} control | |
*/ | |
function initControl(control) { | |
if (!control) { | |
throw new Error('Validation control must not be null.'); | |
} | |
if (!context) { | |
throw new Error('Validation context must not be null.'); | |
} | |
for (var i = 0; i < context.modelValidations.length; i++) { | |
var modelConfig = context.modelValidations[i]; | |
var validationDefinition = findValidationDefinition(control, modelConfig.validationDefinition); | |
if (validationDefinition) { | |
control.applyValidationDefinition(validationDefinition, modelConfig.modelGetter); | |
return; | |
} | |
} | |
}; | |
/** | |
* @param element | |
* @returns {string} | |
*/ | |
function getNgRepeatExpression(element) { | |
var angularElem = angular.element(element); | |
return angularElem.attr('ng-repeat') || angularElem.attr('data-ng-repeat'); | |
} | |
/** | |
* @param expression | |
* @returns {TtNgRepeat} | |
*/ | |
function parseNgRepeat(expression) { | |
// the whole parsing code is taken from angular.js | |
// expression: item in items | (key, value) in items | |
var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/); | |
if (!match) { | |
throw new Error('NgRepeat expression \'' + expression + '\' could not be parsed'); | |
} | |
var lhs = match[1]; // item | (key, value) | |
var rhs = match[2]; // items | |
match = lhs.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/); | |
if (!match) { | |
throw new Error('The left part \'' + lhs + '\' of the NgRepeat expression \'' + expression + '\' could not be parsed.'); | |
} | |
var valueIdentifier = match[3] || match[1]; // item | value | |
var keyIdentifier = match[2]; // key | |
return new TtNgRepeat(rhs, valueIdentifier); | |
} | |
/** | |
* @param {TtValidateController} control | |
*/ | |
function findValidationDefinition(control, validationDefinition) { | |
if (!validationDefinition) | |
return null; | |
if (control.ngRepeats.length > 0) { | |
for (var i = control.ngRepeats.length - 1; i >= 0; i--) { | |
/** @type {TtNgRepeat} */ | |
var parsedNgRepeat = control.ngRepeats[i]; | |
validationDefinition = $parse(parsedNgRepeat.collectionExp)(validationDefinition); | |
if (!validationDefinition) | |
return null; | |
var wrapper = {}; | |
$parse(parsedNgRepeat.valueExp).assign(wrapper, validationDefinition); | |
validationDefinition = wrapper; | |
} | |
} | |
var propertyValidation = $parse(control.ngModelExpression)(validationDefinition); | |
if (propertyValidation && propertyValidation.$validation) { | |
return propertyValidation.$validation; | |
} | |
return null; | |
} | |
function getFirstVariableName(expression) { | |
return expression.split('.')[0].trim(); | |
} | |
}; | |
/** | |
* @param $parse | |
* @param {TtValidationDefinition} ttValidationDefinition | |
* @returns {*} | |
* @constructor | |
*/ | |
function Directive($parse, ttValidationDefinition) { | |
return { | |
restrict: 'A', | |
scope: true, | |
controller: TtValidationController, | |
compile: function (tElement, tAttrs, transclude) { | |
return { | |
pre: function (scope, element, attrs) { | |
scope.isValidationElement = function (angularElem) { | |
if (angularElem.length > 0) | |
angularElem = angularElem[0]; | |
return angularElem === element[0]; | |
} | |
}, | |
post: function (scope, element, attrs) { | |
var options = scope.$eval(attrs.ttValidation); | |
var ctx = { | |
modelValidations: [] | |
}; | |
var configs = []; | |
for (var i = 0; i < options.validations.length; i++) { | |
var validation = options.validations[i]; | |
var valDef; | |
if (angular.isDefined(validation.definition)) { | |
if (!angular.isObject(validation.definition)) | |
throw new Error('The validation definition property must be an object.'); | |
valDef = validation.definition; | |
} else { | |
valDef = ttValidationDefinition.get(validation.key); | |
} | |
var modelCtx = createValidationContext(scope, valDef, validation.model); | |
ctx.modelValidations.push(modelCtx); | |
} | |
scope.init(ctx); | |
} | |
}; | |
function createValidationContext(scope, rules, modelExp) { | |
var modelGetter; | |
if (modelExp) { | |
var modelExp = (modelExp), | |
ngModelGet = $parse(modelExp), | |
ngModelSet = ngModelGet.assign; | |
if (!ngModelSet) | |
throw new Error('Validation model \'' + modelExp + '\' is non-assignable.') | |
var wrappedRules = {}; | |
ngModelSet(wrappedRules, rules); | |
rules = wrappedRules; | |
modelGetter = function () { | |
return ngModelGet(scope); | |
} | |
} else { | |
modelGetter = function () { | |
return scope; | |
}; | |
} | |
return new TtValidationContext(rules, modelGetter) | |
} | |
} | |
}; | |
} | |
app.module.directive("ttValidation", Directive); | |
function TtValidationContext(rules, modelGetter) { | |
this.validationDefinition = rules; | |
this.modelGetter = modelGetter; | |
} | |
function TtNgRepeat(collectionExp, valueExp) { | |
this.collectionExp = collectionExp; | |
this.valueExp = valueExp; | |
} | |
function removeFromArray(array, item) { | |
var index = array.indexOf(item); | |
if (item >= 0) { | |
array.splice(index, 1); | |
} | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment