Created
May 9, 2015 18:02
-
-
Save teropa/3861646d81db399db030 to your computer and use it in GitHub Desktop.
Code for "Inside The AngularJS Directive Compiler"
This file contains hidden or 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
'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