diff --git a/docs/content/guide/decorators.ngdoc b/docs/content/guide/decorators.ngdoc
new file mode 100644
index 000000000000..5422f6d32dc5
--- /dev/null
+++ b/docs/content/guide/decorators.ngdoc
@@ -0,0 +1,486 @@
+@ngdoc overview
+@name Decorators
+@sortOrder 345
+@description
+
+# Decorators in AngularJS
+
+
+ **NOTE:** This guide is targeted towards developers who are already familiar with AngularJS basics.
+ If you're just getting started, we recommend the {@link tutorial/ tutorial} first.
+
+
+## What are decorators?
+
+Decorators are a design pattern that is used to separate modification or *decoration* of a class without modifying the
+original source code. In Angular, decorators are functions that allow a service, directive or filter to be modified
+prior to its usage.
+
+## How to use decorators
+
+There are two ways to register decorators
+
+- `$provide.decorator`, and
+- `module.decorator`
+
+Each provide access to a `$delegate`, which is the instantiated service/directive/filter, prior to being passed to the
+service that required it.
+
+### $provide.decorator
+
+The {@link api/auto/service/$provide#decorator decorator function} allows access to a $delegate of the service once it
+has been instantiated. For example:
+
+```js
+angular.module('myApp', [])
+
+.config([ '$provide', function($provide) {
+
+ $provide.decorator('$log', [
+ '$delegate',
+ function $logDecorator($delegate) {
+
+ var originalWarn = $delegate.warn;
+ $delegate.warn = function decoratedWarn(msg) {
+ msg = 'Decorated Warn: ' + msg;
+ originalWarn.apply($delegate, arguments);
+ };
+
+ return $delegate;
+ }
+ ]);
+}]);
+```
+
+After the `$log` service has been instantiated the decorator is fired. The decorator function has a `$delegate` object
+injected to provide access to the service that matches the selector in the decorator. This `$delegate` will be the
+service you are decorating. The return value of the function *provided to the decorator* will take place of the service,
+directive, or filter being decorated.
+
+
+
+The `$delegate` may be either modified or completely replaced. Given a service `myService` with a method `someFn`, the
+following could all be viable solutions:
+
+
+#### Completely Replace the $delegate
+```js
+angular.module('myApp', [])
+
+.config([ '$provide', function($provide) {
+
+ $provide.decorator('myService', [
+ '$delegate',
+ function myServiceDecorator($delegate) {
+
+ var myDecoratedService = {
+ // new service object to replace myService
+ };
+ return myDecoratedService;
+ }
+ ]);
+}]);
+```
+
+#### Patch the $delegate
+```js
+angular.module('myApp', [])
+
+.config([ '$provide', function($provide) {
+
+ $provide.decorator('myService', [
+ '$delegate',
+ function myServiceDecorator($delegate) {
+
+ var someFn = $delegate.someFn;
+
+ function aNewFn() {
+ // new service function
+ someFn.apply($delegate, arguments);
+ }
+
+ $delegate.someFn = aNewFn;
+ return $delegate;
+ }
+ ]);
+}]);
+```
+
+#### Augment the $delegate
+```js
+angular.module('myApp', [])
+
+.config([ '$provide', function($provide) {
+
+ $provide.decorator('myService', [
+ '$delegate',
+ function myServiceDecorator($delegate) {
+
+ function helperFn() {
+ // an additional fn to add to the service
+ }
+
+ $delegate.aHelpfulAddition = helperFn;
+ return $delegate;
+ }
+ ]);
+}]);
+```
+
+
+ Note that whatever is returned by the decorator function will replace that which is being decorated. For example, a
+ missing return statement will wipe out the entire object being decorated.
+
+
+
+
+Decorators have different rules for different services. This is because services are registered in different ways.
+Services are selected by name, however filters and directives are selected by appending `"Filter"` or `"Directive"` to
+the end of the name. The `$delegate` provided is dictated by the type of service.
+
+| Service Type | Selector | $delegate |
+|--------------|-------------------------------|-----------------------------------------------------------------------|
+| Service | `serviceName` | The `object` or `function` returned by the service |
+| Directive | `directiveName + 'Directive'` | An `Array.`{@link guide/decorators#drtvArray 1} |
+| Filter | `filterName + 'Filter'` | The `function` returned by the filter |
+
+1. Multiple directives may be registered to the same selector/name
+
+
+ **NOTE:** Developers should take care in how and why they are modifying the `$delegate` for the service. Not only
+ should expectations for the consumer be kept, but some functionality (such as directive registration) does not take
+ place after decoration, but during creation/registration of the original service. This means, for example, that
+ an action such as pushing a directive object to a directive `$delegate` will likely result in unexpected behavior.
+
+ Furthermore, great care should be taken when decorating core services, directives, or filters as this may unexpectedly
+ or adversely affect the functionality of the framework.
+
+
+### module.decorator
+
+This {@link api/ng/type/angular.Module#decorator function} is the same as the `$provide.decorator` function except it is
+exposed through the module API. This allows you to separate your decorator patterns from your module config blocks. The
+main caveat here is that you will need to take note the order in which you create your decorators.
+
+Unlike in the module config block (which allows configuration of services prior to their creation), the service must be
+registered prior to the decorator (see {@link guide/providers#provider-recipe Provider Recipe}). For example, the
+following would not work because you are attempting to decorate outside of the configuration phase and the service
+hasn't been created yet:
+
+```js
+// will cause an error since 'someService' hasn't been registered
+angular.module('myApp').decorator('someService', ...);
+
+angular.module('myApp').factory('someService', ...);
+```
+
+## Example Applications
+
+The following sections provide examples each of a service decorator, a directive decorator, and a filter decorator.
+
+### Service Decorator Example
+
+This example shows how we can replace the $log service with our own to display log messages.
+
+
+
+ angular.module('myServiceDecorator', []).
+
+ controller('Ctrl', [
+ '$scope',
+ '$log',
+ '$timeout',
+ function($scope, $log, $timeout) {
+ var types = ['error', 'warn', 'log', 'info' ,'debug'], i;
+
+ for (i = 0; i < types.length; i++) {
+ $log[types[i]](types[i] + ': message ' + (i + 1));
+ }
+
+ $timeout(function() {
+ $log.info('info: message logged in timeout');
+ });
+ }
+ ]).
+
+ directive('myLog', [
+ '$log',
+ function($log) {
+ return {
+ restrict: 'E',
+ template: '',
+ scope: {},
+ compile: function() {
+ return function(scope) {
+ scope.myLog = $log.stack;
+ };
+ }
+ };
+ }
+ ]).
+
+ config([
+ '$provide',
+ function($provide) {
+
+ $provide.decorator('$log', [
+ '$delegate',
+ function logDecorator($delegate) {
+
+ var myLog = {
+ warn: function(msg) {
+ log(msg, 'warn');
+ },
+ error: function(msg) {
+ log(msg, 'error');
+ },
+ info: function(msg) {
+ log(msg, 'info');
+ },
+ debug: function(msg) {
+ log(msg, 'debug');
+ },
+ log: function(msg) {
+ log(msg, 'log');
+ },
+ stack: []
+ };
+
+ function log(msg, type) {
+ myLog.stack.push({ type: type, message: msg.toString() });
+ if (console && console[type]) console[type](msg);
+ }
+
+ return myLog;
+
+ }
+ ]);
+
+ }
+ ]);
+
+
+
+
+
Logs
+
+
+
+
+
+ li.warn { color: yellow; }
+ li.error { color: red; }
+ li.info { color: blue }
+ li.log { color: black }
+ li.debug { color: green }
+
+
+
+ it('should display log messages in dom', function() {
+ element.all(by.repeater('l in myLog')).count().then(function(count) {
+ expect(count).toEqual(6);
+ });
+ });
+
+
+
+### Directive Decorator Example
+
+Failed interpolated expressions in `ng-href` attributes can easily go unnoticed. We can decorate `ngHref` to warn us of
+those conditions.
+
+
+
+ angular.module('urlDecorator', []).
+
+ controller('Ctrl', ['$scope', function ($scope) {
+ $scope.id = 3;
+ $scope.warnCount = 0; // for testing
+ }]).
+
+ config(['$provide', function($provide) {
+
+ // matchExpressions looks for interpolation markup in the directive attribute, extracts the expressions
+ // from that markup (if they exist) and returns an array of those expressions
+ function matchExpressions(str) {
+ var exps = str.match(/{{([^}]+)}}/g);
+
+ // if there isn't any, get out of here
+ if (exps === null) return;
+
+ exps = exps.map(function(exp) {
+ var prop = exp.match(/[^{}]+/);
+ return prop === null ? null : prop[0];
+ });
+
+ return exps;
+ }
+
+ // remember: directives must be selected by appending 'Directive' to the directive selector
+ $provide.decorator('ngHrefDirective', [
+ '$delegate',
+ '$log',
+ '$parse',
+ function($delegate, $log, $parse) {
+
+ // store the original link fn
+ var originalLinkFn = $delegate[0].link;
+
+ // replace the compile fn
+ $delegate[0].compile = function(tElem, tAttr) {
+
+ // store the original exp in the directive attribute for our warning message
+ var originalExp = tAttr.ngHref;
+
+ // get the interpolated expressions
+ var exps = matchExpressions(originalExp);
+
+ // create and store the getters using $parse
+ var getters = exps.map(function(el) {
+ if (el) return $parse(el);
+ });
+
+ return function newLinkFn(scope, elem, attr) {
+ // fire the originalLinkFn
+ originalLinkFn.apply($delegate[0], arguments);
+
+ // observe the directive attr and check the expressions
+ attr.$observe('ngHref', function(val) {
+
+ // if we have getters and getters is an array...
+ if (getters && angular.isArray(getters)) {
+
+ // loop through the getters and process them
+ angular.forEach(getters, function(g, idx) {
+
+ // if val is truthy, then the warning won't log
+ var val = angular.isFunction(g) ? g(scope) : true;
+ if (!val) {
+ $log.warn('NgHref Warning: "' + exps[idx] + '" in the expression "' + originalExp +
+ '" is falsy!');
+
+ scope.warnCount++; // for testing
+ }
+
+ });
+
+ }
+
+ });
+
+ };
+
+ };
+
+ // get rid of the old link function since we return a link function in compile
+ delete $delegate[0].link;
+
+ // return the $delegate
+ return $delegate;
+
+ }
+
+ ]);
+
+ }]);
+
+
+
+
+
+
+
+ it('should warn when an expression in the interpolated value is falsy', function() {
+ var id3 = element(by.id('id3'));
+ var id8 = element(by.id('id8'));
+ var someOther = element(by.id('someOtherId'));
+ var someOther5 = element(by.id('someOtherId5'));
+
+ expect(id3.getText()).toEqual('View Product 3');
+ expect(id3.getAttribute('href')).toContain('/products/3/view');
+
+ expect(id8.getText()).toEqual('View Product 8');
+ expect(id8.getAttribute('href')).toContain('/products/8/view');
+
+ expect(someOther.getText()).toEqual('View Product');
+ expect(someOther.getAttribute('href')).toContain('/products//view');
+
+ expect(someOther5.getText()).toEqual('View Product 5');
+ expect(someOther5.getAttribute('href')).toContain('/products/5/view');
+
+ expect(element(by.binding('warnCount')).getText()).toEqual('Warn Count: 1');
+ });
+
+
+
+### Filter Decorator Example
+
+Let's say we have created an app that uses the default format for many of our `Date` filters. Suddenly requirements have
+changed (that never happens) and we need all of our default dates to be `'shortDate'` instead of `'mediumDate'`.
+
+
+
+ angular.module('filterDecorator', []).
+
+ controller('Ctrl', ['$scope', function ($scope) {
+ $scope.genesis = new Date(2010, 0, 5);
+ $scope.ngConf = new Date(2016, 4, 4);
+ }]).
+
+ config(['$provide', function($provide) {
+
+ $provide.decorator('dateFilter', [
+ '$delegate',
+ function dateDecorator($delegate) {
+
+ // store the original filter
+ var originalFilter = $delegate;
+
+ // return our filter
+ return shortDateDefault;
+
+ // shortDateDefault sets the format to shortDate if it is falsy
+ function shortDateDefault(date, format, timezone) {
+ if (!format) format = 'shortDate';
+
+ // return the result of the original filter
+ return originalFilter(date, format, timezone);
+ }
+
+ }
+
+ ]);
+
+ }]);
+
+
+
+
+
Initial Commit default to short date: {{ genesis | date }}
+
ng-conf 2016 default short date: {{ ngConf | date }}
+
ng-conf 2016 with full date format: {{ ngConf | date:'fullDate' }}
+
+
+
+
+ it('should default date filter to short date format', function() {
+ expect(element(by.id('genesis')).getText())
+ .toMatch(/Initial Commit default to short date: \d{1,2}\/\d{1,2}\/\d{2}/);
+ });
+
+ it('should still allow dates to be formatted', function() {
+ expect(element(by.id('ngConf')).getText())
+ .toMatch(/ng-conf 2016 with full date format\: [A-Za-z]+, [A-Za-z]+ \d{1,2}, \d{4}/);
+ });
+
+