Skip to content

Instantly share code, notes, and snippets.

@teropa
Created May 9, 2015 18:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save teropa/3861646d81db399db030 to your computer and use it in GitHub Desktop.
Save teropa/3861646d81db399db030 to your computer and use it in GitHub Desktop.
Code for "Inside The AngularJS Directive Compiler"
'use strict';
function $CompileProvider($provide) {
var directives = {};
this.directive = function(name, factory) {
if (!directives.hasOwnProperty(name)) {
directives[name] = [factory];
$provide.factory(
name + 'Directive',
function($injector) {
var factories = directives[name];
return factories.map(function(factory) {
var directive = $injector.invoke(factory);
if (directive.link && !directive.compile) {
directive.compile = function() {
return directive.link;
};
}
return directive;
});
}
);
} else {
directives[name].pusb(factory);
}
};
this.$get = function($injector, $parse) {
function collectElementDirectives(element) {
var elName = element[0].nodeName;
var directiveName = _.camelCase(elName);
var fullDirName = directiveName + 'Directive';
if (directives.hasOwnProperty(directiveName)) {
return $injector.get(fullDirName);
} else {
return [];
}
}
function collectAttributeDirectives(element) {
var result = [];
_.forEach(element[0].attributes, function(attr) {
var directiveName = _.camelCase(attr.name);
var fullDirName = directiveName + 'Directive';
if (directives.hasOwnProperty(directiveName)) {
var matches = $injector.get(fullDirName);
result = result.concat(matches);
}
});
return result;
}
function collectClassDirectives(element) {
var result = [];
_.forEach(element[0].classList, function(cName) {
var directiveName = _.camelCase(cName);
var fullDirName = directiveName + 'Directive';
if (directives.hasOwnProperty(directiveName)) {
var matches = $injector.get(fullDirName);
result = result.concat(matches);
}
});
return result;
}
function collectDirectives(element) {
return
collectElementDirectives(element)
.concat(collectAttributeDirectives(element))
.concat(collectClassDirectives(element));
}
function compileNode(element) {
var directives = collectDirectives(element),
linkFns = [],
newScopeDirective,
newIsoScopeDirective;
directives.forEach(function(directive) {
var linkFn = directive.compile(element);
linkFn.directive = directive;
linkFns.push(linkFn);
if (directive.scope) {
if (newScopeDirective || newIsoScopeDirective) {
throw 'More than one directive asking for new/isolated scope';
}
if (_.isObject(directive.scope)) {
newIsoScopeDirective = directive;
} else {
newScopeDirective = directive;
}
}
});
var childLinkFns = _.map(element.children(), compileNode);
return function nodeLinkFn(scope) {
var isoScope;
if (newScopeDirective) {
scope = scope.$new();
}
if (newIsoScopeDirective) {
isoScope = scope.$new(true);
_.forEach(
newIsoScopeDirective.scope,
function(spec, scopeName) {
var attrName = spec.match(/^=(.*)/)[1];
var denormalized = _.kebabCase(attrName);
var expr = element.attr(denormalized);
var exprFn = $parse(expr);
var parentValue;
scope.$watch(function() {
var newParentValue = exprFn(scope);
var childValue = isoScope[scopeName];
if (newParentValue !== childValue) {
if (newParentValue !== parentValue) {
isoScope[scopeName] = newParentValue;
} else {
exprFn.assign(scope, childValue);
newParentValue = childValue;
}
}
parentValue = newParentValue;
});
}
);
}
childLinkFns.forEach(function(childLinkFn) {
childLinkFn(scope);
});
linkFns.forEach(function(linkFn) {
var isIso = (linkFn.directive === newIsoScopeDirective);
linkFn(isIso ? isoScope : scope, element);
});
}
}
return function $compile(element) {
return compileNode(element);
};
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment