aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorIgor Minar2012-03-08 15:00:38 -0800
committerIgor Minar2012-03-08 22:29:34 -0800
commitf54db2ccda399f2677e4ca7588018cb31545a2b4 (patch)
tree29ef2f8f834544c84cea1a82e3d08679358fb992 /src
parentdd7b0f56fcd9785f7fccae8c4f088a8f3e7b125e (diff)
downloadangular.js-f54db2ccda399f2677e4ca7588018cb31545a2b4.tar.bz2
chore(directives,widgets): reorg the code under directive/ dir
Diffstat (limited to 'src')
-rw-r--r--src/directive/a.js29
-rw-r--r--src/directive/booleanAttrDirs.js (renamed from src/markups.js)15
-rw-r--r--src/directive/directives.js11
-rw-r--r--src/directive/form.js (renamed from src/widget/form.js)2
-rw-r--r--src/directive/input.js (renamed from src/widget/input.js)0
-rw-r--r--src/directive/ngBind.js249
-rw-r--r--src/directive/ngClass.js143
-rw-r--r--src/directive/ngCloak.js61
-rw-r--r--src/directive/ngController.js103
-rw-r--r--src/directive/ngEventDirs.js222
-rw-r--r--src/directive/ngInclude.js119
-rw-r--r--src/directive/ngInit.js37
-rw-r--r--src/directive/ngNonBindable.js34
-rw-r--r--src/directive/ngPluralize.js204
-rw-r--r--src/directive/ngRepeat.js182
-rw-r--r--src/directive/ngShowHide.js80
-rw-r--r--src/directive/ngStyle.js42
-rw-r--r--src/directive/ngSwitch.js110
-rw-r--r--src/directive/ngTransclude.js58
-rw-r--r--src/directive/ngView.js169
-rw-r--r--src/directive/script.js42
-rw-r--r--src/directive/select.js (renamed from src/widget/select.js)0
-rw-r--r--src/directive/style.js6
-rw-r--r--src/directives.js1031
-rw-r--r--src/widgets.js876
25 files changed, 1916 insertions, 1909 deletions
diff --git a/src/directive/a.js b/src/directive/a.js
new file mode 100644
index 00000000..bd56f3be
--- /dev/null
+++ b/src/directive/a.js
@@ -0,0 +1,29 @@
+'use strict';
+
+/*
+ * Modifies the default behavior of html A tag, so that the default action is prevented when href
+ * attribute is empty.
+ *
+ * The reasoning for this change is to allow easy creation of action links with ng:click without
+ * changing the location or causing page reloads, e.g.:
+ * <a href="" ng:click="model.$save()">Save</a>
+ */
+var htmlAnchorDirective = valueFn({
+ restrict: 'E',
+ compile: function(element, attr) {
+ // turn <a href ng:click="..">link</a> into a link in IE
+ // but only if it doesn't have name attribute, in which case it's an anchor
+ if (!attr.href) {
+ attr.$set('href', '');
+ }
+
+ return function(scope, element) {
+ element.bind('click', function(event){
+ // if we have no href url, then don't navigate anywhere.
+ if (!element.attr('href')) {
+ event.preventDefault();
+ }
+ });
+ }
+ }
+});
diff --git a/src/markups.js b/src/directive/booleanAttrDirs.js
index 36b03131..06b85823 100644
--- a/src/markups.js
+++ b/src/directive/booleanAttrDirs.js
@@ -1,6 +1,5 @@
'use strict';
-
/**
* @ngdoc directive
* @name angular.module.ng.$compileProvider.directive.ng:href
@@ -267,3 +266,17 @@
* @param {template} template any string which can contain '{{}}' markup.
*/
+
+function ngAttributeAliasDirective(propName, attrName) {
+ ngAttributeAliasDirectives[directiveNormalize('ng-' + attrName)] = valueFn(
+ function(scope, element, attr) {
+ attr.$observe(directiveNormalize('ng-' + attrName), function(value) {
+ attr.$set(attrName, value);
+ });
+ }
+ );
+}
+
+var ngAttributeAliasDirectives = {};
+forEach(BOOLEAN_ATTR, ngAttributeAliasDirective);
+ngAttributeAliasDirective(null, 'src');
diff --git a/src/directive/directives.js b/src/directive/directives.js
new file mode 100644
index 00000000..123645f9
--- /dev/null
+++ b/src/directive/directives.js
@@ -0,0 +1,11 @@
+'use strict';
+
+function ngDirective(directive) {
+ if (isFunction(directive)) {
+ directive = {
+ link: directive
+ }
+ }
+ directive.restrict = directive.restrict || 'AC';
+ return valueFn(directive);
+};
diff --git a/src/widget/form.js b/src/directive/form.js
index e3823f41..c3e6b21d 100644
--- a/src/widget/form.js
+++ b/src/directive/form.js
@@ -17,7 +17,7 @@
*
* @description
* `FormController` keeps track of all its widgets as well as state of them form, such as being valid/invalid or dirty/pristine.
- *
+ *
* Each {@link angular.module.ng.$compileProvider.directive.form form} directive creates an instance
* of `FormController`.
*
diff --git a/src/widget/input.js b/src/directive/input.js
index af446c6b..af446c6b 100644
--- a/src/widget/input.js
+++ b/src/directive/input.js
diff --git a/src/directive/ngBind.js b/src/directive/ngBind.js
new file mode 100644
index 00000000..37fabb94
--- /dev/null
+++ b/src/directive/ngBind.js
@@ -0,0 +1,249 @@
+'use strict';
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:bind
+ *
+ * @description
+ * The `ng:bind` attribute tells Angular to replace the text content of the specified HTML element
+ * with the value of a given expression, and to update the text content when the value of that
+ * expression changes.
+ *
+ * Typically, you don't use `ng:bind` directly, but instead you use the double curly markup like
+ * `{{ expression }}` and let the Angular compiler transform it to
+ * `<span ng:bind="expression"></span>` when the template is compiled.
+ *
+ * @element ANY
+ * @param {expression} expression {@link guide/dev_guide.expressions Expression} to evaluate.
+ *
+ * @example
+ * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
+ <doc:example>
+ <doc:source>
+ <script>
+ function Ctrl($scope) {
+ $scope.name = 'Whirled';
+ }
+ </script>
+ <div ng:controller="Ctrl">
+ Enter name: <input type="text" ng:model="name"> <br/>
+ Hello <span ng:bind="name"></span>!
+ </div>
+ </doc:source>
+ <doc:scenario>
+ it('should check ng:bind', function() {
+ expect(using('.doc-example-live').binding('name')).toBe('Whirled');
+ using('.doc-example-live').input('name').enter('world');
+ expect(using('.doc-example-live').binding('name')).toBe('world');
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+var ngBindDirective = ngDirective(function(scope, element, attr) {
+ element.addClass('ng-binding').data('$binding', attr.ngBind);
+ scope.$watch(attr.ngBind, function(value) {
+ element.text(value == undefined ? '' : value);
+ });
+});
+
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:bind-html-unsafe
+ *
+ * @description
+ * Creates a binding that will innerHTML the result of evaluating the `expression` into the current
+ * element. *The innerHTML-ed content will not be sanitized!* You should use this directive only if
+ * {@link angular.module.ng.$compileProvider.directive.ng:bind-html ng:bind-html} directive is too
+ * restrictive and when you absolutely trust the source of the content you are binding to.
+ *
+ * See {@link angular.module.ng.$sanitize $sanitize} docs for examples.
+ *
+ * @element ANY
+ * @param {expression} expression {@link guide/dev_guide.expressions Expression} to evaluate.
+ */
+var ngBindHtmlUnsafeDirective = ngDirective(function(scope, element, attr) {
+ element.addClass('ng-binding').data('$binding', attr.ngBindHtmlUnsafe);
+ scope.$watch(attr.ngBindHtmlUnsafe, function(value) {
+ element.html(value == undefined ? '' : value);
+ });
+});
+
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:bind-html
+ *
+ * @description
+ * Creates a binding that will sanitize the result of evaluating the `expression` with the
+ * {@link angular.module.ng.$sanitize $sanitize} service and innerHTML the result into the current
+ * element.
+ *
+ * See {@link angular.module.ng.$sanitize $sanitize} docs for examples.
+ *
+ * @element ANY
+ * @param {expression} expression {@link guide/dev_guide.expressions Expression} to evaluate.
+ */
+var ngBindHtmlDirective = ['$sanitize', function($sanitize) {
+ return function(scope, element, attr) {
+ element.addClass('ng-binding').data('$binding', attr.ngBindHtml);
+ scope.$watch(attr.ngBindHtml, function(value) {
+ if (value = $sanitize(value)) {
+ element.html(value);
+ }
+ });
+ }
+}];
+
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:bind-template
+ *
+ * @description
+ * The `ng:bind-template` attribute specifies that the element
+ * text should be replaced with the template in ng:bind-template.
+ * Unlike ng:bind the ng:bind-template can contain multiple `{{` `}}`
+ * expressions. (This is required since some HTML elements
+ * can not have SPAN elements such as TITLE, or OPTION to name a few.)
+ *
+ * @element ANY
+ * @param {string} template of form
+ * <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
+ *
+ * @example
+ * Try it here: enter text in text box and watch the greeting change.
+ <doc:example>
+ <doc:source>
+ <script>
+ function Ctrl($scope) {
+ $scope.salutation = 'Hello';
+ $scope.name = 'World';
+ }
+ </script>
+ <div ng:controller="Ctrl">
+ Salutation: <input type="text" ng:model="salutation"><br/>
+ Name: <input type="text" ng:model="name"><br/>
+ <pre ng:bind-template="{{salutation}} {{name}}!"></pre>
+ </div>
+ </doc:source>
+ <doc:scenario>
+ it('should check ng:bind', function() {
+ expect(using('.doc-example-live').binding('salutation')).
+ toBe('Hello');
+ expect(using('.doc-example-live').binding('name')).
+ toBe('World');
+ using('.doc-example-live').input('salutation').enter('Greetings');
+ using('.doc-example-live').input('name').enter('user');
+ expect(using('.doc-example-live').binding('salutation')).
+ toBe('Greetings');
+ expect(using('.doc-example-live').binding('name')).
+ toBe('user');
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
+ return function(scope, element, attr) {
+ // TODO: move this to scenario runner
+ var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
+ element.addClass('ng-binding').data('$binding', interpolateFn);
+ attr.$observe('ngBindTemplate', function(value) {
+ element.text(value);
+ });
+ }
+}];
+
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:bind-attr
+ *
+ * @description
+ * The `ng:bind-attr` attribute specifies that a
+ * {@link guide/dev_guide.templates.databinding databinding} should be created between a particular
+ * element attribute and a given expression. Unlike `ng:bind`, the `ng:bind-attr` contains one or
+ * more JSON key value pairs; each pair specifies an attribute and the
+ * {@link guide/dev_guide.expressions expression} to which it will be mapped.
+ *
+ * Instead of writing `ng:bind-attr` statements in your HTML, you can use double-curly markup to
+ * specify an <tt ng:non-bindable>{{expression}}</tt> for the value of an attribute.
+ * At compile time, the attribute is translated into an
+ * `<span ng:bind-attr="{attr:expression}"></span>`.
+ *
+ * The following HTML snippet shows how to specify `ng:bind-attr`:
+ * <pre>
+ * <a ng:bind-attr='{"href":"http://www.google.com/search?q={{query}}"}'>Google</a>
+ * </pre>
+ *
+ * This is cumbersome, so as we mentioned using double-curly markup is a prefered way of creating
+ * this binding:
+ * <pre>
+ * <a href="http://www.google.com/search?q={{query}}">Google</a>
+ * </pre>
+ *
+ * During compilation, the template with attribute markup gets translated to the ng:bind-attr form
+ * mentioned above.
+ *
+ * _Note_: You might want to consider using {@link angular.module.ng.$compileProvider.directive.ng:href ng:href} instead of
+ * `href` if the binding is present in the main application template (`index.html`) and you want to
+ * make sure that a user is not capable of clicking on raw/uncompiled link.
+ *
+ *
+ * @element ANY
+ * @param {string} attribute_json one or more JSON key-value pairs representing
+ * the attributes to replace with expressions. Each key matches an attribute
+ * which needs to be replaced. Each value is a text template of
+ * the attribute with the embedded
+ * <tt ng:non-bindable>{{expression}}</tt>s. Any number of
+ * key-value pairs can be specified.
+ *
+ * @example
+ * Enter a search string in the Live Preview text box and then click "Google". The search executes instantly.
+ <doc:example>
+ <doc:source>
+ <script>
+ function Ctrl($scope) {
+ $scope.query = 'AngularJS';
+ }
+ </script>
+ <div ng:controller="Ctrl">
+ Google for:
+ <input type="text" ng:model="query"/>
+ <a ng:bind-attr='{"href":"http://www.google.com/search?q={{query}}"}'>
+ Google
+ </a> (ng:bind-attr) |
+ <a href="http://www.google.com/search?q={{query}}">Google</a>
+ (curly binding in attribute val)
+ </div>
+ </doc:source>
+ <doc:scenario>
+ it('should check ng:bind-attr', function() {
+ expect(using('.doc-example-live').element('a').attr('href')).
+ toBe('http://www.google.com/search?q=AngularJS');
+ using('.doc-example-live').input('query').enter('google');
+ expect(using('.doc-example-live').element('a').attr('href')).
+ toBe('http://www.google.com/search?q=google');
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+
+var ngBindAttrDirective = ['$interpolate', function($interpolate) {
+ return function(scope, element, attr) {
+ var lastValue = {};
+ var interpolateFns = {};
+ scope.$watch(function() {
+ var values = scope.$eval(attr.ngBindAttr);
+ for(var key in values) {
+ var exp = values[key],
+ fn = (interpolateFns[exp] ||
+ (interpolateFns[values[key]] = $interpolate(exp))),
+ value = fn(scope);
+ if (lastValue[key] !== value) {
+ attr.$set(key, lastValue[key] = value);
+ }
+ }
+ });
+ }
+}];
diff --git a/src/directive/ngClass.js b/src/directive/ngClass.js
new file mode 100644
index 00000000..2016d04f
--- /dev/null
+++ b/src/directive/ngClass.js
@@ -0,0 +1,143 @@
+'use strict';
+
+function classDirective(name, selector) {
+ name = 'ngClass' + name;
+ return ngDirective(function(scope, element, attr) {
+ scope.$watch(attr[name], function(newVal, oldVal) {
+ if (selector === true || scope.$index % 2 === selector) {
+ if (oldVal && (newVal !== oldVal)) {
+ if (isObject(oldVal) && !isArray(oldVal))
+ oldVal = map(oldVal, function(v, k) { if (v) return k });
+ element.removeClass(isArray(oldVal) ? oldVal.join(' ') : oldVal);
+ }
+ if (isObject(newVal) && !isArray(newVal))
+ newVal = map(newVal, function(v, k) { if (v) return k });
+ if (newVal) element.addClass(isArray(newVal) ? newVal.join(' ') : newVal); }
+ }, true);
+ });
+}
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:class
+ *
+ * @description
+ * The `ng:class` allows you to set CSS class on HTML element dynamically by databinding an
+ * expression that represents all classes to be added.
+ *
+ * The directive won't add duplicate classes if a particular class was already set.
+ *
+ * When the expression changes, the previously added classes are removed and only then the classes
+ * new classes are added.
+ *
+ * @element ANY
+ * @param {expression} expression {@link guide/dev_guide.expressions Expression} to eval. The result
+ * of the evaluation can be a string representing space delimited class
+ * names, an array, or a map of class names to boolean values.
+ *
+ * @example
+ <doc:example>
+ <doc:source>
+ <input type="button" value="set" ng:click="myVar='ng-invalid'">
+ <input type="button" value="clear" ng:click="myVar=''">
+ <br>
+ <span ng:class="myVar">Sample Text &nbsp;&nbsp;&nbsp;&nbsp;</span>
+ </doc:source>
+ <doc:scenario>
+ it('should check ng:class', function() {
+ expect(element('.doc-example-live span').prop('className')).not().
+ toMatch(/ng-invalid/);
+
+ using('.doc-example-live').element(':button:first').click();
+
+ expect(element('.doc-example-live span').prop('className')).
+ toMatch(/ng-invalid/);
+
+ using('.doc-example-live').element(':button:last').click();
+
+ expect(element('.doc-example-live span').prop('className')).not().
+ toMatch(/ng-invalid/);
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+var ngClassDirective = classDirective('', true);
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:class-odd
+ *
+ * @description
+ * The `ng:class-odd` and `ng:class-even` works exactly as
+ * {@link angular.module.ng.$compileProvider.directive.ng:class ng:class}, except it works in conjunction with `ng:repeat` and
+ * takes affect only on odd (even) rows.
+ *
+ * This directive can be applied only within a scope of an
+ * {@link angular.module.ng.$compileProvider.directive.ng:repeat ng:repeat}.
+ *
+ * @element ANY
+ * @param {expression} expression {@link guide/dev_guide.expressions Expression} to eval. The result
+ * of the evaluation can be a string representing space delimited class names or an array.
+ *
+ * @example
+ <doc:example>
+ <doc:source>
+ <ol ng:init="names=['John', 'Mary', 'Cate', 'Suz']">
+ <li ng:repeat="name in names">
+ <span ng:class-odd="'ng-format-negative'"
+ ng:class-even="'ng-invalid'">
+ {{name}} &nbsp; &nbsp; &nbsp;
+ </span>
+ </li>
+ </ol>
+ </doc:source>
+ <doc:scenario>
+ it('should check ng:class-odd and ng:class-even', function() {
+ expect(element('.doc-example-live li:first span').prop('className')).
+ toMatch(/ng-format-negative/);
+ expect(element('.doc-example-live li:last span').prop('className')).
+ toMatch(/ng-invalid/);
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+var ngClassOddDirective = classDirective('Odd', 0);
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:class-even
+ *
+ * @description
+ * The `ng:class-odd` and `ng:class-even` works exactly as
+ * {@link angular.module.ng.$compileProvider.directive.ng:class ng:class}, except it works in conjunction with `ng:repeat` and
+ * takes affect only on odd (even) rows.
+ *
+ * This directive can be applied only within a scope of an
+ * {@link angular.module.ng.$compileProvider.directive.ng:repeat ng:repeat}.
+ *
+ * @element ANY
+ * @param {expression} expression {@link guide/dev_guide.expressions Expression} to eval. The result
+ * of the evaluation can be a string representing space delimited class names or an array.
+ *
+ * @example
+ <doc:example>
+ <doc:source>
+ <ol ng:init="names=['John', 'Mary', 'Cate', 'Suz']">
+ <li ng:repeat="name in names">
+ <span ng:class-odd="'odd'" ng:class-even="'even'">
+ {{name}} &nbsp; &nbsp; &nbsp;
+ </span>
+ </li>
+ </ol>
+ </doc:source>
+ <doc:scenario>
+ it('should check ng:class-odd and ng:class-even', function() {
+ expect(element('.doc-example-live li:first span').prop('className')).
+ toMatch(/odd/);
+ expect(element('.doc-example-live li:last span').prop('className')).
+ toMatch(/even/);
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+var ngClassEvenDirective = classDirective('Even', 1);
diff --git a/src/directive/ngCloak.js b/src/directive/ngCloak.js
new file mode 100644
index 00000000..b4fe6708
--- /dev/null
+++ b/src/directive/ngCloak.js
@@ -0,0 +1,61 @@
+'use strict';
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:cloak
+ *
+ * @description
+ * The `ng:cloak` directive is used to prevent the Angular html template from being briefly
+ * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
+ * directive to avoid the undesirable flicker effect caused by the html template display.
+ *
+ * The directive can be applied to the `<body>` element, but typically a fine-grained application is
+ * prefered in order to benefit from progressive rendering of the browser view.
+ *
+ * `ng:cloak` works in cooperation with a css rule that is embedded within `angular.js` and
+ * `angular.min.js` files. Following is the css rule:
+ *
+ * <pre>
+ * [ng\:cloak], .ng-cloak {
+ * display: none;
+ * }
+ * </pre>
+ *
+ * When this css rule is loaded by the browser, all html elements (including their children) that
+ * are tagged with the `ng:cloak` directive are hidden. When Angular comes across this directive
+ * during the compilation of the template it deletes the `ng:cloak` element attribute, which
+ * makes the compiled element visible.
+ *
+ * For the best result, `angular.js` script must be loaded in the head section of the html file;
+ * alternatively, the css rule (above) must be included in the external stylesheet of the
+ * application.
+ *
+ * Legacy browsers, like IE7, do not provide attribute selector support (added in CSS 2.1) so they
+ * cannot match the `[ng\:cloak]` selector. To work around this limitation, you must add the css
+ * class `ng-cloak` in addition to `ng:cloak` directive as shown in the example below.
+ *
+ * @element ANY
+ *
+ * @example
+ <doc:example>
+ <doc:source>
+ <div id="template1" ng:cloak>{{ 'hello' }}</div>
+ <div id="template2" ng:cloak class="ng-cloak">{{ 'hello IE7' }}</div>
+ </doc:source>
+ <doc:scenario>
+ it('should remove the template directive and css class', function() {
+ expect(element('.doc-example-live #template1').attr('ng:cloak')).
+ not().toBeDefined();
+ expect(element('.doc-example-live #template2').attr('ng:cloak')).
+ not().toBeDefined();
+ });
+ </doc:scenario>
+ </doc:example>
+ *
+ */
+var ngCloakDirective = ngDirective({
+ compile: function(element, attr) {
+ attr.$set('ngCloak', undefined);
+ element.removeClass('ng-cloak');
+ }
+});
diff --git a/src/directive/ngController.js b/src/directive/ngController.js
new file mode 100644
index 00000000..e17a97cd
--- /dev/null
+++ b/src/directive/ngController.js
@@ -0,0 +1,103 @@
+'use strict';
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:controller
+ *
+ * @description
+ * The `ng:controller` directive assigns behavior to a scope. This is a key aspect of how angular
+ * supports the principles behind the Model-View-Controller design pattern.
+ *
+ * MVC components in angular:
+ *
+ * * Model — The Model is data in scope properties; scopes are attached to the DOM.
+ * * View — The template (HTML with data bindings) is rendered into the View.
+ * * Controller — The `ng:controller` directive specifies a Controller class; the class has
+ * methods that typically express the business logic behind the application.
+ *
+ * Note that an alternative way to define controllers is via the `{@link angular.module.ng.$route}`
+ * service.
+ *
+ * @element ANY
+ * @scope
+ * @param {expression} expression Name of a globally accessible constructor function or an
+ * {@link guide/dev_guide.expressions expression} that on the current scope evaluates to a
+ * constructor function.
+ *
+ * @example
+ * Here is a simple form for editing user contact information. Adding, removing, clearing, and
+ * greeting are methods declared on the controller (see source tab). These methods can
+ * easily be called from the angular markup. Notice that the scope becomes the `this` for the
+ * controller's instance. This allows for easy access to the view data from the controller. Also
+ * notice that any changes to the data are automatically reflected in the View without the need
+ * for a manual update.
+ <doc:example>
+ <doc:source>
+ <script type="text/javascript">
+ function SettingsController($scope) {
+ $scope.name = "John Smith";
+ $scope.contacts = [
+ {type:'phone', value:'408 555 1212'},
+ {type:'email', value:'john.smith@example.org'} ];
+
+ $scope.greet = function() {
+ alert(this.name);
+ };
+
+ $scope.addContact = function() {
+ this.contacts.push({type:'email', value:'yourname@example.org'});
+ };
+
+ $scope.removeContact = function(contactToRemove) {
+ var index = this.contacts.indexOf(contactToRemove);
+ this.contacts.splice(index, 1);
+ };
+
+ $scope.clearContact = function(contact) {
+ contact.type = 'phone';
+ contact.value = '';
+ };
+ }
+ </script>
+ <div ng:controller="SettingsController">
+ Name: <input type="text" ng:model="name"/>
+ [ <a href="" ng:click="greet()">greet</a> ]<br/>
+ Contact:
+ <ul>
+ <li ng:repeat="contact in contacts">
+ <select ng:model="contact.type">
+ <option>phone</option>
+ <option>email</option>
+ </select>
+ <input type="text" ng:model="contact.value"/>
+ [ <a href="" ng:click="clearContact(contact)">clear</a>
+ | <a href="" ng:click="removeContact(contact)">X</a> ]
+ </li>
+ <li>[ <a href="" ng:click="addContact()">add</a> ]</li>
+ </ul>
+ </div>
+ </doc:source>
+ <doc:scenario>
+ it('should check controller', function() {
+ expect(element('.doc-example-live div>:input').val()).toBe('John Smith');
+ expect(element('.doc-example-live li:nth-child(1) input').val())
+ .toBe('408 555 1212');
+ expect(element('.doc-example-live li:nth-child(2) input').val())
+ .toBe('john.smith@example.org');
+
+ element('.doc-example-live li:first a:contains("clear")').click();
+ expect(element('.doc-example-live li:first input').val()).toBe('');
+
+ element('.doc-example-live li:last a:contains("add")').click();
+ expect(element('.doc-example-live li:nth-child(3) input').val())
+ .toBe('yourname@example.org');
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+var ngControllerDirective = ['$controller', '$window', function($controller, $window) {
+ return {
+ scope: true,
+ controller: '@'
+ }
+}];
diff --git a/src/directive/ngEventDirs.js b/src/directive/ngEventDirs.js
new file mode 100644
index 00000000..0815229d
--- /dev/null
+++ b/src/directive/ngEventDirs.js
@@ -0,0 +1,222 @@
+'use strict';
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:click
+ *
+ * @description
+ * The ng:click allows you to specify custom behavior when
+ * element is clicked.
+ *
+ * @element ANY
+ * @param {expression} expression {@link guide/dev_guide.expressions Expression} to evaluate upon
+ * click. (Event object is available as `$event`)
+ *
+ * @example
+ <doc:example>
+ <doc:source>
+ <button ng:click="count = count + 1" ng:init="count=0">
+ Increment
+ </button>
+ count: {{count}}
+ </doc:source>
+ <doc:scenario>
+ it('should check ng:click', function() {
+ expect(binding('count')).toBe('0');
+ element('.doc-example-live :button').click();
+ expect(binding('count')).toBe('1');
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+/*
+ * A directive that allows creation of custom onclick handlers that are defined as angular
+ * expressions and are compiled and executed within the current scope.
+ *
+ * Events that are handled via these handler are always configured not to propagate further.
+ */
+var ngEventDirectives = {};
+forEach(
+ 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave'.split(' '),
+ function(name) {
+ var directiveName = directiveNormalize('ng-' + name);
+ ngEventDirectives[directiveName] = ['$parse', function($parse) {
+ return function(scope, element, attr) {
+ var fn = $parse(attr[directiveName]);
+ element.bind(lowercase(name), function(event) {
+ scope.$apply(function() {
+ fn(scope, {$event:event});
+ });
+ });
+ };
+ }];
+ }
+);
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:dblclick
+ *
+ * @description
+ * The ng:dblclick allows you to specify custom behavior on dblclick event.
+ *
+ * @element ANY
+ * @param {expression} expression {@link guide/dev_guide.expressions Expression} to evaluate upon
+ * dblclick. (Event object is available as `$event`)
+ *
+ * @example
+ * See {@link angular.module.ng.$compileProvider.directive.ng:click ng:click}
+ */
+
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:mousedown
+ *
+ * @description
+ * The ng:mousedown allows you to specify custom behavior on mousedown event.
+ *
+ * @element ANY
+ * @param {expression} expression {@link guide/dev_guide.expressions Expression} to evaluate upon
+ * mousedown. (Event object is available as `$event`)
+ *
+ * @example
+ * See {@link angular.module.ng.$compileProvider.directive.ng:click ng:click}
+ */
+
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:mouseup
+ *
+ * @description
+ * Specify custom behavior on mouseup event.
+ *
+ * @element ANY
+ * @param {expression} expression {@link guide/dev_guide.expressions Expression} to evaluate upon
+ * mouseup. (Event object is available as `$event`)
+ *
+ * @example
+ * See {@link angular.module.ng.$compileProvider.directive.ng:click ng:click}
+ */
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:mouseover
+ *
+ * @description
+ * Specify custom behavior on mouseover event.
+ *
+ * @element ANY
+ * @param {expression} expression {@link guide/dev_guide.expressions Expression} to evaluate upon
+ * mouseover. (Event object is available as `$event`)
+ *
+ * @example
+ * See {@link angular.module.ng.$compileProvider.directive.ng:click ng:click}
+ */
+
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:mouseenter
+ *
+ * @description
+ * Specify custom behavior on mouseenter event.
+ *
+ * @element ANY
+ * @param {expression} expression {@link guide/dev_guide.expressions Expression} to evaluate upon
+ * mouseenter. (Event object is available as `$event`)
+ *
+ * @example
+ * See {@link angular.module.ng.$compileProvider.directive.ng:click ng:click}
+ */
+
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:mouseleave
+ *
+ * @description
+ * Specify custom behavior on mouseleave event.
+ *
+ * @element ANY
+ * @param {expression} expression {@link guide/dev_guide.expressions Expression} to evaluate upon
+ * mouseleave. (Event object is available as `$event`)
+ *
+ * @example
+ * See {@link angular.module.ng.$compileProvider.directive.ng:click ng:click}
+ */
+
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:mousemove
+ *
+ * @description
+ * Specify custom behavior on mousemove event.
+ *
+ * @element ANY
+ * @param {expression} expression {@link guide/dev_guide.expressions Expression} to evaluate upon
+ * mousemove. (Event object is available as `$event`)
+ *
+ * @example
+ * See {@link angular.module.ng.$compileProvider.directive.ng:click ng:click}
+ */
+
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:submit
+ *
+ * @description
+ * Enables binding angular expressions to onsubmit events.
+ *
+ * Additionally it prevents the default action (which for form means sending the request to the
+ * server and reloading the current page).
+ *
+ * @element form
+ * @param {expression} expression {@link guide/dev_guide.expressions Expression} to eval.
+ *
+ * @example
+ <doc:example>
+ <doc:source>
+ <script>
+ function Ctrl($scope) {
+ $scope.list = [];
+ $scope.text = 'hello';
+ $scope.submit = function() {
+ if (this.text) {
+ this.list.push(this.text);
+ this.text = '';
+ }
+ };
+ }
+ </script>
+ <form ng:submit="submit()" ng:controller="Ctrl">
+ Enter text and hit enter:
+ <input type="text" ng:model="text" name="text" />
+ <input type="submit" id="submit" value="Submit" />
+ <pre>list={{list}}</pre>
+ </form>
+ </doc:source>
+ <doc:scenario>
+ it('should check ng:submit', function() {
+ expect(binding('list')).toBe('[]');
+ element('.doc-example-live #submit').click();
+ expect(binding('list')).toBe('["hello"]');
+ expect(input('text').val()).toBe('');
+ });
+ it('should ignore empty strings', function() {
+ expect(binding('list')).toBe('[]');
+ element('.doc-example-live #submit').click();
+ element('.doc-example-live #submit').click();
+ expect(binding('list')).toBe('["hello"]');
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+var ngSubmitDirective = ngDirective(function(scope, element, attrs) {
+ element.bind('submit', function() {
+ scope.$apply(attrs.ngSubmit);
+ });
+});
diff --git a/src/directive/ngInclude.js b/src/directive/ngInclude.js
new file mode 100644
index 00000000..08b14488
--- /dev/null
+++ b/src/directive/ngInclude.js
@@ -0,0 +1,119 @@
+'use strict';
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:include
+ * @restrict EA
+ *
+ * @description
+ * Fetches, compiles and includes an external HTML fragment.
+ *
+ * Keep in mind that Same Origin Policy applies to included resources
+ * (e.g. ng:include won't work for file:// access).
+ *
+ * @scope
+ *
+ * @param {string} src angular expression evaluating to URL. If the source is a string constant,
+ * make sure you wrap it in quotes, e.g. `src="'myPartialTemplate.html'"`.
+ * @param {Scope=} [scope=new_child_scope] optional expression which evaluates to an
+ * instance of angular.module.ng.$rootScope.Scope to set the HTML fragment to.
+ * @param {string=} onload Expression to evaluate when a new partial is loaded.
+ *
+ * @param {string=} autoscroll Whether `ng:include` should call {@link angular.module.ng.$anchorScroll
+ * $anchorScroll} to scroll the viewport after the content is loaded.
+ *
+ * - If the attribute is not set, disable scrolling.
+ * - If the attribute is set without value, enable scrolling.
+ * - Otherwise enable scrolling only if the expression evaluates to truthy value.
+ *
+ * @example
+ <doc:example>
+ <doc:source jsfiddle="false">
+ <script>
+ function Ctrl($scope) {
+ $scope.templates =
+ [ { name: 'template1.html', url: 'examples/ng-include/template1.html'}
+ , { name: 'template2.html', url: 'examples/ng-include/template2.html'} ];
+ $scope.template = $scope.templates[0];
+ }
+ </script>
+ <div ng:controller="Ctrl">
+ <select ng:model="template" ng:options="t.name for t in templates">
+ <option value="">(blank)</option>
+ </select>
+ url of the template: <tt><a href="{{template.url}}">{{template.url}}</a></tt>
+ <hr/>
+ <div ng-include src="template.url"></div>
+ </div>
+ </doc:source>
+ <doc:scenario>
+ it('should load template1.html', function() {
+ expect(element('.doc-example-live [ng-include]').text()).
+ toBe('Content of template1.html\n');
+ });
+ it('should load template2.html', function() {
+ select('template').option('1');
+ expect(element('.doc-example-live [ng-include]').text()).
+ toBe('Content of template2.html\n');
+ });
+ it('should change to blank', function() {
+ select('template').option('');
+ expect(element('.doc-example-live [ng-include]').text()).toEqual('');
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile',
+ function($http, $templateCache, $anchorScroll, $compile) {
+ return {
+ restrict: 'EA',
+ compile: function(element, attr) {
+ var srcExp = attr.src,
+ scopeExp = attr.scope || '',
+ autoScrollExp = attr.autoscroll;
+
+ return function(scope, element, attr) {
+ var changeCounter = 0,
+ childScope;
+
+ function incrementChange() { changeCounter++;}
+ scope.$watch(srcExp, incrementChange);
+ scope.$watch(function() {
+ var includeScope = scope.$eval(scopeExp);
+ if (includeScope) return includeScope.$id;
+ }, incrementChange);
+ scope.$watch(function() {return changeCounter;}, function(newChangeCounter) {
+ var src = scope.$eval(srcExp),
+ useScope = scope.$eval(scopeExp);
+
+ function clearContent() {
+ // if this callback is still desired
+ if (newChangeCounter === changeCounter) {
+ if (childScope) childScope.$destroy();
+ childScope = null;
+ element.html('');
+ }
+ }
+
+ if (src) {
+ $http.get(src, {cache: $templateCache}).success(function(response) {
+ // if this callback is still desired
+ if (newChangeCounter === changeCounter) {
+ element.html(response);
+ if (childScope) childScope.$destroy();
+ childScope = useScope ? useScope : scope.$new();
+ $compile(element.contents())(childScope);
+ if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
+ $anchorScroll();
+ }
+ scope.$emit('$contentLoaded');
+ }
+ }).error(clearContent);
+ } else {
+ clearContent();
+ }
+ });
+ };
+ }
+ }
+}];
diff --git a/src/directive/ngInit.js b/src/directive/ngInit.js
new file mode 100644
index 00000000..cdab1cdb
--- /dev/null
+++ b/src/directive/ngInit.js
@@ -0,0 +1,37 @@
+'use strict';
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:init
+ *
+ * @description
+ * The `ng:init` attribute specifies initialization tasks to be executed
+ * before the template enters execution mode during bootstrap.
+ *
+ * @element ANY
+ * @param {expression} expression {@link guide/dev_guide.expressions Expression} to eval.
+ *
+ * @example
+ <doc:example>
+ <doc:source>
+ <div ng:init="greeting='Hello'; person='World'">
+ {{greeting}} {{person}}!
+ </div>
+ </doc:source>
+ <doc:scenario>
+ it('should check greeting', function() {
+ expect(binding('greeting')).toBe('Hello');
+ expect(binding('person')).toBe('World');
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+var ngInitDirective = ngDirective({
+ compile: function() {
+ return {
+ pre: function(scope, element, attrs) {
+ scope.$eval(attrs.ngInit);
+ }
+ }
+ }
+});
diff --git a/src/directive/ngNonBindable.js b/src/directive/ngNonBindable.js
new file mode 100644
index 00000000..b9857afa
--- /dev/null
+++ b/src/directive/ngNonBindable.js
@@ -0,0 +1,34 @@
+'use strict';
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:non-bindable
+ *
+ * @description
+ * Sometimes it is necessary to write code which looks like bindings but which should be left alone
+ * by angular. Use `ng:non-bindable` to make angular ignore a chunk of HTML.
+ *
+ * Note: `ng:non-bindable` looks like a directive, but is actually an attribute widget.
+ *
+ * @element ANY
+ *
+ * @example
+ * In this example there are two location where a simple binding (`{{}}`) is present, but the one
+ * wrapped in `ng:non-bindable` is left alone.
+ *
+ * @example
+ <doc:example>
+ <doc:source>
+ <div>Normal: {{1 + 2}}</div>
+ <div ng:non-bindable>Ignored: {{1 + 2}}</div>
+ </doc:source>
+ <doc:scenario>
+ it('should check ng:non-bindable', function() {
+ expect(using('.doc-example-live').binding('1 + 2')).toBe('3');
+ expect(using('.doc-example-live').element('div:last').text()).
+ toMatch(/1 \+ 2/);
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+var ngNonBindableDirective = ngDirective({ terminal: true });
diff --git a/src/directive/ngPluralize.js b/src/directive/ngPluralize.js
new file mode 100644
index 00000000..4a6f4a7e
--- /dev/null
+++ b/src/directive/ngPluralize.js
@@ -0,0 +1,204 @@
+'use strict';
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:pluralize
+ * @restrict EA
+ *
+ * @description
+ * # Overview
+ * ng:pluralize is a widget that displays messages according to en-US localization rules.
+ * These rules are bundled with angular.js and the rules can be overridden
+ * (see {@link guide/dev_guide.i18n Angular i18n} dev guide). You configure ng:pluralize by
+ * specifying the mappings between
+ * {@link http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
+ * plural categories} and the strings to be displayed.
+ *
+ * # Plural categories and explicit number rules
+ * There are two
+ * {@link http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
+ * plural categories} in Angular's default en-US locale: "one" and "other".
+ *
+ * While a pural category may match many numbers (for example, in en-US locale, "other" can match
+ * any number that is not 1), an explicit number rule can only match one number. For example, the
+ * explicit number rule for "3" matches the number 3. You will see the use of plural categories
+ * and explicit number rules throughout later parts of this documentation.
+ *
+ * # Configuring ng:pluralize
+ * You configure ng:pluralize by providing 2 attributes: `count` and `when`.
+ * You can also provide an optional attribute, `offset`.
+ *
+ * The value of the `count` attribute can be either a string or an {@link guide/dev_guide.expressions
+ * Angular expression}; these are evaluated on the current scope for its binded value.
+ *
+ * The `when` attribute specifies the mappings between plural categories and the actual
+ * string to be displayed. The value of the attribute should be a JSON object so that Angular
+ * can interpret it correctly.
+ *
+ * The following example shows how to configure ng:pluralize:
+ *
+ * <pre>
+ * <ng:pluralize count="personCount"
+ when="{'0': 'Nobody is viewing.',
+ * 'one': '1 person is viewing.',
+ * 'other': '{} people are viewing.'}">
+ * </ng:pluralize>
+ *</pre>
+ *
+ * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
+ * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
+ * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
+ * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
+ * show "a dozen people are viewing".
+ *
+ * You can use a set of closed braces(`{}`) as a placeholder for the number that you want substituted
+ * into pluralized strings. In the previous example, Angular will replace `{}` with
+ * <span ng:non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
+ * for <span ng:non-bindable>{{numberExpression}}</span>.
+ *
+ * # Configuring ng:pluralize with offset
+ * The `offset` attribute allows further customization of pluralized text, which can result in
+ * a better user experience. For example, instead of the message "4 people are viewing this document",
+ * you might display "John, Kate and 2 others are viewing this document".
+ * The offset attribute allows you to offset a number by any desired value.
+ * Let's take a look at an example:
+ *
+ * <pre>
+ * <ng:pluralize count="personCount" offset=2
+ * when="{'0': 'Nobody is viewing.',
+ * '1': '{{person1}} is viewing.',
+ * '2': '{{person1}} and {{person2}} are viewing.',
+ * 'one': '{{person1}}, {{person2}} and one other person are viewing.',
+ * 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
+ * </ng:pluralize>
+ * </pre>
+ *
+ * Notice that we are still using two plural categories(one, other), but we added
+ * three explicit number rules 0, 1 and 2.
+ * When one person, perhaps John, views the document, "John is viewing" will be shown.
+ * When three people view the document, no explicit number rule is found, so
+ * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
+ * In this case, plural category 'one' is matched and "John, Marry and one other person are viewing"
+ * is shown.
+ *
+ * Note that when you specify offsets, you must provide explicit number rules for
+ * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
+ * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
+ * plural categories "one" and "other".
+ *
+ * @param {string|expression} count The variable to be bounded to.
+ * @param {string} when The mapping between plural category to its correspoding strings.
+ * @param {number=} offset Offset to deduct from the total number.
+ *
+ * @example
+ <doc:example>
+ <doc:source>
+ <script>
+ function Ctrl($scope) {
+ $scope.person1 = 'Igor';
+ $scope.person2 = 'Misko';
+ $scope.personCount = 1;
+ }
+ </script>
+ <div ng:controller="Ctrl">
+ Person 1:<input type="text" ng:model="person1" value="Igor" /><br/>
+ Person 2:<input type="text" ng:model="person2" value="Misko" /><br/>
+ Number of People:<input type="text" ng:model="personCount" value="1" /><br/>
+
+ <!--- Example with simple pluralization rules for en locale --->
+ Without Offset:
+ <ng-pluralize count="personCount"
+ when="{'0': 'Nobody is viewing.',
+ 'one': '1 person is viewing.',
+ 'other': '{} people are viewing.'}">
+ </ng-pluralize><br>
+
+ <!--- Example with offset --->
+ With Offset(2):
+ <ng-pluralize count="personCount" offset=2
+ when="{'0': 'Nobody is viewing.',
+ '1': '{{person1}} is viewing.',
+ '2': '{{person1}} and {{person2}} are viewing.',
+ 'one': '{{person1}}, {{person2}} and one other person are viewing.',
+ 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
+ </ng-pluralize>
+ </div>
+ </doc:source>
+ <doc:scenario>
+ it('should show correct pluralized string', function() {
+ expect(element('.doc-example-live ng-pluralize:first').text()).
+ toBe('1 person is viewing.');
+ expect(element('.doc-example-live ng-pluralize:last').text()).
+ toBe('Igor is viewing.');
+
+ using('.doc-example-live').input('personCount').enter('0');
+ expect(element('.doc-example-live ng-pluralize:first').text()).
+ toBe('Nobody is viewing.');
+ expect(element('.doc-example-live ng-pluralize:last').text()).
+ toBe('Nobody is viewing.');
+
+ using('.doc-example-live').input('personCount').enter('2');
+ expect(element('.doc-example-live ng-pluralize:first').text()).
+ toBe('2 people are viewing.');
+ expect(element('.doc-example-live ng-pluralize:last').text()).
+ toBe('Igor and Misko are viewing.');
+
+ using('.doc-example-live').input('personCount').enter('3');
+ expect(element('.doc-example-live ng-pluralize:first').text()).
+ toBe('3 people are viewing.');
+ expect(element('.doc-example-live ng-pluralize:last').text()).
+ toBe('Igor, Misko and one other person are viewing.');
+
+ using('.doc-example-live').input('personCount').enter('4');
+ expect(element('.doc-example-live ng-pluralize:first').text()).
+ toBe('4 people are viewing.');
+ expect(element('.doc-example-live ng-pluralize:last').text()).
+ toBe('Igor, Misko and 2 other people are viewing.');
+ });
+
+ it('should show data-binded names', function() {
+ using('.doc-example-live').input('personCount').enter('4');
+ expect(element('.doc-example-live ng-pluralize:last').text()).
+ toBe('Igor, Misko and 2 other people are viewing.');
+
+ using('.doc-example-live').input('person1').enter('Di');
+ using('.doc-example-live').input('person2').enter('Vojta');
+ expect(element('.doc-example-live ng-pluralize:last').text()).
+ toBe('Di, Vojta and 2 other people are viewing.');
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) {
+ var BRACE = /{}/g;
+ return {
+ restrict: 'EA',
+ link: function(scope, element, attr) {
+ var numberExp = attr.count,
+ whenExp = element.attr(attr.$attr.when), // this is because we have {{}} in attrs
+ offset = attr.offset || 0,
+ whens = scope.$eval(whenExp),
+ whensExpFns = {};
+
+ forEach(whens, function(expression, key) {
+ whensExpFns[key] =
+ $interpolate(expression.replace(BRACE, '{{' + numberExp + '-' + offset + '}}'));
+ });
+
+ scope.$watch(function() {
+ var value = parseFloat(scope.$eval(numberExp));
+
+ if (!isNaN(value)) {
+ //if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise,
+ //check it against pluralization rules in $locale service
+ if (!whens[value]) value = $locale.pluralCat(value - offset);
+ return whensExpFns[value](scope, element, true);
+ } else {
+ return '';
+ }
+ }, function(newVal) {
+ element.text(newVal);
+ });
+ }
+ };
+}];
diff --git a/src/directive/ngRepeat.js b/src/directive/ngRepeat.js
new file mode 100644
index 00000000..7cab7c40
--- /dev/null
+++ b/src/directive/ngRepeat.js
@@ -0,0 +1,182 @@
+'use strict';
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:repeat
+ *
+ * @description
+ * The `ng:repeat` widget instantiates a template once per item from a collection. Each template
+ * instance gets its own scope, where the given loop variable is set to the current collection item,
+ * and `$index` is set to the item index or key.
+ *
+ * Special properties are exposed on the local scope of each template instance, including:
+ *
+ * * `$index` – `{number}` – iterator offset of the repeated element (0..length-1)
+ * * `$position` – `{string}` – position of the repeated element in the iterator. One of:
+ * * `'first'`,
+ * * `'middle'`
+ * * `'last'`
+ *
+ * Note: Although `ng:repeat` looks like a directive, it is actually an attribute widget.
+ *
+ * @element ANY
+ * @scope
+ * @priority 1000
+ * @param {string} repeat_expression The expression indicating how to enumerate a collection. Two
+ * formats are currently supported:
+ *
+ * * `variable in expression` – where variable is the user defined loop variable and `expression`
+ * is a scope expression giving the collection to enumerate.
+ *
+ * For example: `track in cd.tracks`.
+ *
+ * * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
+ * and `expression` is the scope expression giving the collection to enumerate.
+ *
+ * For example: `(name, age) in {'adam':10, 'amalie':12}`.
+ *
+ * @example
+ * This example initializes the scope to a list of names and
+ * then uses `ng:repeat` to display every person:
+ <doc:example>
+ <doc:source>
+ <div ng:init="friends = [{name:'John', age:25}, {name:'Mary', age:28}]">
+ I have {{friends.length}} friends. They are:
+ <ul>
+ <li ng:repeat="friend in friends">
+ [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
+ </li>
+ </ul>
+ </div>
+ </doc:source>
+ <doc:scenario>
+ it('should check ng:repeat', function() {
+ var r = using('.doc-example-live').repeater('ul li');
+ expect(r.count()).toBe(2);
+ expect(r.row(0)).toEqual(["1","John","25"]);
+ expect(r.row(1)).toEqual(["2","Mary","28"]);
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+var ngRepeatDirective = ngDirective({
+ transclude: 'element',
+ priority: 1000,
+ terminal: true,
+ compile: function(element, attr, linker) {
+ return function(scope, iterStartElement, attr){
+ var expression = attr.ngRepeat;
+ var match = expression.match(/^\s*(.+)\s+in\s+(.*)\s*$/),
+ lhs, rhs, valueIdent, keyIdent;
+ if (! match) {
+ throw Error("Expected ng:repeat in form of '_item_ in _collection_' but got '" +
+ expression + "'.");
+ }
+ lhs = match[1];
+ rhs = match[2];
+ match = lhs.match(/^([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\)$/);
+ if (!match) {
+ throw Error("'item' in 'item in collection' should be identifier or (key, value) but got '" +
+ keyValue + "'.");
+ }
+ valueIdent = match[3] || match[1];
+ keyIdent = match[2];
+
+ // Store a list of elements from previous run. This is a hash where key is the item from the
+ // iterator, and the value is an array of objects with following properties.
+ // - scope: bound scope
+ // - element: previous element.
+ // - index: position
+ // We need an array of these objects since the same object can be returned from the iterator.
+ // We expect this to be a rare case.
+ var lastOrder = new HashQueueMap();
+ scope.$watch(function(scope){
+ var index, length,
+ collection = scope.$eval(rhs),
+ collectionLength = size(collection, true),
+ childScope,
+ // Same as lastOrder but it has the current state. It will become the
+ // lastOrder on the next iteration.
+ nextOrder = new HashQueueMap(),
+ key, value, // key/value of iteration
+ array, last, // last object information {scope, element, index}
+ cursor = iterStartElement; // current position of the node
+
+ if (!isArray(collection)) {
+ // if object, extract keys, sort them and use to determine order of iteration over obj props
+ array = [];
+ for(key in collection) {
+ if (collection.hasOwnProperty(key) && key.charAt(0) != '$') {
+ array.push(key);
+ }
+ }
+ array.sort();
+ } else {
+ array = collection || [];
+ }
+
+ // we are not using forEach for perf reasons (trying to avoid #call)
+ for (index = 0, length = array.length; index < length; index++) {
+ key = (collection === array) ? index : array[index];
+ value = collection[key];
+ last = lastOrder.shift(value);
+ if (last) {
+ // if we have already seen this object, then we need to reuse the
+ // associated scope/element
+ childScope = last.scope;
+ nextOrder.push(value, last);
+
+ if (index === last.index) {
+ // do nothing
+ cursor = last.element;
+ } else {
+ // existing item which got moved
+ last.index = index;
+ // This may be a noop, if the element is next, but I don't know of a good way to
+ // figure this out, since it would require extra DOM access, so let's just hope that
+ // the browsers realizes that it is noop, and treats it as such.
+ cursor.after(last.element);
+ cursor = last.element;
+ }
+ } else {
+ // new item which we don't know about
+ childScope = scope.$new();
+ }
+
+ childScope[valueIdent] = value;
+ if (keyIdent) childScope[keyIdent] = key;
+ childScope.$index = index;
+ childScope.$position = index === 0 ?
+ 'first' :
+ (index == collectionLength - 1 ? 'last' : 'middle');
+
+ if (!last) {
+ linker(childScope, function(clone){
+ cursor.after(clone);
+ last = {
+ scope: childScope,
+ element: (cursor = clone),
+ index: index
+ };
+ nextOrder.push(value, last);
+ });
+ }
+ }
+
+ //shrink children
+ for (key in lastOrder) {
+ if (lastOrder.hasOwnProperty(key)) {
+ array = lastOrder[key];
+ while(array.length) {
+ value = array.pop();
+ value.element.remove();
+ value.scope.$destroy();
+ }
+ }
+ }
+
+ lastOrder = nextOrder;
+ });
+ };
+ }
+});
diff --git a/src/directive/ngShowHide.js b/src/directive/ngShowHide.js
new file mode 100644
index 00000000..0060ec80
--- /dev/null
+++ b/src/directive/ngShowHide.js
@@ -0,0 +1,80 @@
+'use strict';
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:show
+ *
+ * @description
+ * The `ng:show` and `ng:hide` directives show or hide a portion of the DOM tree (HTML)
+ * conditionally.
+ *
+ * @element ANY
+ * @param {expression} expression If the {@link guide/dev_guide.expressions expression} is truthy
+ * then the element is shown or hidden respectively.
+ *
+ * @example
+ <doc:example>
+ <doc:source>
+ Click me: <input type="checkbox" ng:model="checked"><br/>
+ Show: <span ng:show="checked">I show up when your checkbox is checked.</span> <br/>
+ Hide: <span ng:hide="checked">I hide when your checkbox is checked.</span>
+ </doc:source>
+ <doc:scenario>
+ it('should check ng:show / ng:hide', function() {
+ expect(element('.doc-example-live span:first:hidden').count()).toEqual(1);
+ expect(element('.doc-example-live span:last:visible').count()).toEqual(1);
+
+ input('checked').check();
+
+ expect(element('.doc-example-live span:first:visible').count()).toEqual(1);
+ expect(element('.doc-example-live span:last:hidden').count()).toEqual(1);
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+//TODO(misko): refactor to remove element from the DOM
+var ngShowDirective = ngDirective(function(scope, element, attr){
+ scope.$watch(attr.ngShow, function(value){
+ element.css('display', toBoolean(value) ? '' : 'none');
+ });
+});
+
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:hide
+ *
+ * @description
+ * The `ng:hide` and `ng:show` directives hide or show a portion
+ * of the HTML conditionally.
+ *
+ * @element ANY
+ * @param {expression} expression If the {@link guide/dev_guide.expressions expression} truthy then
+ * the element is shown or hidden respectively.
+ *
+ * @example
+ <doc:example>
+ <doc:source>
+ Click me: <input type="checkbox" ng:model="checked"><br/>
+ Show: <span ng:show="checked">I show up when you checkbox is checked?</span> <br/>
+ Hide: <span ng:hide="checked">I hide when you checkbox is checked?</span>
+ </doc:source>
+ <doc:scenario>
+ it('should check ng:show / ng:hide', function() {
+ expect(element('.doc-example-live span:first:hidden').count()).toEqual(1);
+ expect(element('.doc-example-live span:last:visible').count()).toEqual(1);
+
+ input('checked').check();
+
+ expect(element('.doc-example-live span:first:visible').count()).toEqual(1);
+ expect(element('.doc-example-live span:last:hidden').count()).toEqual(1);
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+//TODO(misko): refactor to remove element from the DOM
+var ngHideDirective = ngDirective(function(scope, element, attr){
+ scope.$watch(attr.ngHide, function(value){
+ element.css('display', toBoolean(value) ? 'none' : '');
+ });
+});
diff --git a/src/directive/ngStyle.js b/src/directive/ngStyle.js
new file mode 100644
index 00000000..cd2c1e3c
--- /dev/null
+++ b/src/directive/ngStyle.js
@@ -0,0 +1,42 @@
+'use strict';
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:style
+ *
+ * @description
+ * The ng:style allows you to set CSS style on an HTML element conditionally.
+ *
+ * @element ANY
+ * @param {expression} expression {@link guide/dev_guide.expressions Expression} which evals to an
+ * object whose keys are CSS style names and values are corresponding values for those CSS
+ * keys.
+ *
+ * @example
+ <doc:example>
+ <doc:source>
+ <input type="button" value="set" ng:click="myStyle={color:'red'}">
+ <input type="button" value="clear" ng:click="myStyle={}">
+ <br/>
+ <span ng:style="myStyle">Sample Text</span>
+ <pre>myStyle={{myStyle}}</pre>
+ </doc:source>
+ <doc:scenario>
+ it('should check ng:style', function() {
+ expect(element('.doc-example-live span').css('color')).toBe('rgb(0, 0, 0)');
+ element('.doc-example-live :button[value=set]').click();
+ expect(element('.doc-example-live span').css('color')).toBe('rgb(255, 0, 0)');
+ element('.doc-example-live :button[value=clear]').click();
+ expect(element('.doc-example-live span').css('color')).toBe('rgb(0, 0, 0)');
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+var ngStyleDirective = ngDirective(function(scope, element, attr) {
+ scope.$watch(attr.ngStyle, function(newStyles, oldStyles) {
+ if (oldStyles && (newStyles !== oldStyles)) {
+ forEach(oldStyles, function(val, style) { element.css(style, '');});
+ }
+ if (newStyles) element.css(newStyles);
+ }, true);
+});
diff --git a/src/directive/ngSwitch.js b/src/directive/ngSwitch.js
new file mode 100644
index 00000000..0b2475f3
--- /dev/null
+++ b/src/directive/ngSwitch.js
@@ -0,0 +1,110 @@
+'use strict';
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:switch
+ * @restrict EA
+ *
+ * @description
+ * Conditionally change the DOM structure.
+ *
+ * @usageContent
+ * <any ng:switch-when="matchValue1">...</any>
+ * <any ng:switch-when="matchValue2">...</any>
+ * ...
+ * <any ng:switch-default>...</any>
+ *
+ * @scope
+ * @param {*} on expression to match against <tt>ng:switch-when</tt>.
+ * @paramDescription
+ * On child elments add:
+ *
+ * * `ng:switch-when`: the case statement to match against. If match then this
+ * case will be displayed.
+ * * `ng:switch-default`: the default case when no other casses match.
+ *
+ * @example
+ <doc:example>
+ <doc:source>
+ <script>
+ function Ctrl($scope) {
+ $scope.items = ['settings', 'home', 'other'];
+ $scope.selection = $scope.items[0];
+ }
+ </script>
+ <div ng:controller="Ctrl">
+ <select ng:model="selection" ng:options="item for item in items">
+ </select>
+ <tt>selection={{selection}}</tt>
+ <hr/>
+ <ng:switch on="selection" >
+ <div ng:switch-when="settings">Settings Div</div>
+ <span ng:switch-when="home">Home Span</span>
+ <span ng:switch-default>default</span>
+ </ng:switch>
+ </div>
+ </doc:source>
+ <doc:scenario>
+ it('should start in settings', function() {
+ expect(element('.doc-example-live ng\\:switch').text()).toMatch(/Settings Div/);
+ });
+ it('should change to home', function() {
+ select('selection').option('home');
+ expect(element('.doc-example-live ng\\:switch').text()).toMatch(/Home Span/);
+ });
+ it('should select deafault', function() {
+ select('selection').option('other');
+ expect(element('.doc-example-live ng\\:switch').text()).toMatch(/default/);
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+var NG_SWITCH = 'ng-switch';
+var ngSwitchDirective = valueFn({
+ restrict: 'EA',
+ compile: function(element, attr) {
+ var watchExpr = attr.ngSwitch || attr.on,
+ cases = {};
+
+ element.data(NG_SWITCH, cases);
+ return function(scope, element){
+ var selectedTransclude,
+ selectedElement;
+
+ scope.$watch(watchExpr, function(value) {
+ if (selectedElement) {
+ selectedElement.remove();
+ selectedElement = null;
+ }
+ if ((selectedTransclude = cases['!' + value] || cases['?'])) {
+ scope.$eval(attr.change);
+ selectedTransclude(scope.$new(), function(caseElement, scope) {
+ selectedElement = caseElement;
+ element.append(caseElement);
+ element.bind('$destroy', bind(scope, scope.$destroy));
+ });
+ }
+ });
+ };
+ }
+});
+
+var ngSwitchWhenDirective = ngDirective({
+ transclude: 'element',
+ priority: 500,
+ compile: function(element, attrs, transclude) {
+ var cases = element.inheritedData(NG_SWITCH);
+ assertArg(cases);
+ cases['!' + attrs.ngSwitchWhen] = transclude;
+ }
+});
+
+var ngSwitchDefaultDirective = ngDirective({
+ transclude: 'element',
+ priority: 500,
+ compile: function(element, attrs, transclude) {
+ var cases = element.inheritedData(NG_SWITCH);
+ assertArg(cases);
+ cases['?'] = transclude;
+ }
+});
diff --git a/src/directive/ngTransclude.js b/src/directive/ngTransclude.js
new file mode 100644
index 00000000..16587f30
--- /dev/null
+++ b/src/directive/ngTransclude.js
@@ -0,0 +1,58 @@
+'use strict';
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:transclude
+ *
+ * @description
+ * Insert the transcluded DOM here.
+ *
+ * @element ANY
+ *
+ * @example
+ <doc:example module="transclude">
+ <doc:source>
+ <script>
+ function Ctrl($scope) {
+ $scope.title = 'Lorem Ipsum';
+ $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
+ }
+
+ angular.module('transclude', [])
+ .directive('pane', function(){
+ return {
+ restrict: 'E',
+ transclude: true,
+ scope: 'isolate',
+ locals: { title:'bind' },
+ template: '<div style="border: 1px solid black;">' +
+ '<div style="background-color: gray">{{title}}</div>' +
+ '<div ng-transclude></div>' +
+ '</div>'
+ };
+ });
+ </script>
+ <div ng:controller="Ctrl">
+ <input ng:model="title"><br>
+ <textarea ng:model="text"></textarea> <br/>
+ <pane title="{{title}}">{{text}}</pane>
+ </div>
+ </doc:source>
+ <doc:scenario>
+ it('should have transcluded', function() {
+ input('title').enter('TITLE');
+ input('text').enter('TEXT');
+ expect(binding('title')).toEqual('TITLE');
+ expect(binding('text')).toEqual('TEXT');
+ });
+ </doc:scenario>
+ </doc:example>
+ *
+ */
+var ngTranscludeDirective = ngDirective({
+ controller: ['$transclude', '$element', function($transclude, $element) {
+ $transclude(function(clone) {
+ $element.append(clone);
+ });
+ }]
+});
diff --git a/src/directive/ngView.js b/src/directive/ngView.js
new file mode 100644
index 00000000..3c589354
--- /dev/null
+++ b/src/directive/ngView.js
@@ -0,0 +1,169 @@
+'use strict';
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:view
+ * @restrict ECA
+ *
+ * @description
+ * # Overview
+ * `ng:view` is a directive that complements the {@link angular.module.ng.$route $route} service by
+ * including the rendered template of the current route into the main layout (`index.html`) file.
+ * Every time the current route changes, the included view changes with it according to the
+ * configuration of the `$route` service.
+ *
+ * @scope
+ * @example
+ <doc:example module="ngView">
+ <doc:source>
+ <script type="text/ng-template" id="examples/book.html">
+ controller: {{name}}<br />
+ Book Id: {{params.bookId}}<br />
+ </script>
+
+ <script type="text/ng-template" id="examples/chapter.html">
+ controller: {{name}}<br />
+ Book Id: {{params.bookId}}<br />
+ Chapter Id: {{params.chapterId}}
+ </script>
+
+ <script>
+ angular.module('ngView', [], function($routeProvider, $locationProvider) {
+ $routeProvider.when('/Book/:bookId', {
+ template: 'examples/book.html',
+ controller: BookCntl
+ });
+ $routeProvider.when('/Book/:bookId/ch/:chapterId', {
+ template: 'examples/chapter.html',
+ controller: ChapterCntl
+ });
+
+ // configure html5 to get links working on jsfiddle
+ $locationProvider.html5Mode(true);
+ });
+
+ function MainCntl($scope, $route, $routeParams, $location) {
+ $scope.$route = $route;
+ $scope.$location = $location;
+ $scope.$routeParams = $routeParams;
+ }
+
+ function BookCntl($scope, $routeParams) {
+ $scope.name = "BookCntl";
+ $scope.params = $routeParams;
+ }
+
+ function ChapterCntl($scope, $routeParams) {
+ $scope.name = "ChapterCntl";
+ $scope.params = $routeParams;
+ }
+ </script>
+
+ <div ng:controller="MainCntl">
+ Choose:
+ <a href="/Book/Moby">Moby</a> |
+ <a href="/Book/Moby/ch/1">Moby: Ch1</a> |
+ <a href="/Book/Gatsby">Gatsby</a> |
+ <a href="/Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
+ <a href="/Book/Scarlet">Scarlet Letter</a><br/>
+
+ <ng:view></ng:view>
+ <hr />
+
+ <pre>$location.path() = {{$location.path()}}</pre>
+ <pre>$route.current.template = {{$route.current.template}}</pre>
+ <pre>$route.current.params = {{$route.current.params}}</pre>
+ <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
+ <pre>$routeParams = {{$routeParams}}</pre>
+ </div>
+ </doc:source>
+ <doc:scenario>
+ it('should load and compile correct template', function() {
+ element('a:contains("Moby: Ch1")').click();
+ var content = element('.doc-example-live ng\\:view').text();
+ expect(content).toMatch(/controller\: ChapterCntl/);
+ expect(content).toMatch(/Book Id\: Moby/);
+ expect(content).toMatch(/Chapter Id\: 1/);
+
+ element('a:contains("Scarlet")').click();
+ content = element('.doc-example-live ng\\:view').text();
+ expect(content).toMatch(/controller\: BookCntl/);
+ expect(content).toMatch(/Book Id\: Scarlet/);
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$compile',
+ '$controller',
+ function($http, $templateCache, $route, $anchorScroll, $compile,
+ $controller) {
+ return {
+ restrict: 'ECA',
+ terminal: true,
+ link: function(scope, element) {
+ var changeCounter = 0,
+ lastScope;
+
+ scope.$on('$afterRouteChange', function(event, next, previous) {
+ changeCounter++;
+ });
+
+ scope.$watch(function() {return changeCounter;}, function(newChangeCounter) {
+ var template = $route.current && $route.current.template;
+
+ function destroyLastScope() {
+ if (lastScope) {
+ lastScope.$destroy();
+ lastScope = null;
+ }
+ }
+
+ function clearContent() {
+ // ignore callback if another route change occured since
+ if (newChangeCounter == changeCounter) {
+ element.html('');
+ }
+ destroyLastScope();
+ }
+
+ if (template) {
+ $http.get(template, {cache: $templateCache}).success(function(response) {
+ // ignore callback if another route change occured since
+ if (newChangeCounter == changeCounter) {
+ element.html(response);
+ destroyLastScope();
+
+ var link = $compile(element.contents()),
+ current = $route.current;
+
+ lastScope = current.scope = scope.$new();
+ if (current.controller) {
+ $controller(current.controller, {$scope: lastScope});
+ }
+
+ link(lastScope);
+ lastScope.$emit('$contentLoaded');
+
+ // $anchorScroll might listen on event...
+ $anchorScroll();
+ }
+ }).error(clearContent);
+ } else {
+ clearContent();
+ }
+ });
+ }
+ };
+}];
+
+
+var onloadDirective = valueFn({
+ restrict: 'AC',
+ link: function(scope, elm, attr) {
+ var onloadExp = attr.onload || ''; //workaround for jquery bug #7537)
+
+ scope.$on('$contentLoaded', function(event) {
+ scope.$eval(onloadExp);
+ });
+ }
+});
diff --git a/src/directive/script.js b/src/directive/script.js
new file mode 100644
index 00000000..f0affaf9
--- /dev/null
+++ b/src/directive/script.js
@@ -0,0 +1,42 @@
+'use strict';
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.script
+ *
+ * @description
+ * Load content of a script tag, with type `text/ng-template`, into `$templateCache`, so that the
+ * template can be used by `ng:include`, `ng:view` or directive templates.
+ *
+ * @restrict E
+ *
+ * @example
+ <doc:example>
+ <doc:source>
+ <script type="text/ng-template" id="/tpl.html">
+ Content of the template.
+ </script>
+
+ <a ng:click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
+ <div id="tpl-content" ng-include src="currentTpl"></div>
+ </doc:source>
+ <doc:scenario>
+ it('should load template defined inside script tag', function() {
+ element('#tpl-link').click();
+ expect(element('#tpl-content').text()).toMatch(/Content of the template/);
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+var scriptDirective = ['$templateCache', function($templateCache) {
+ return {
+ restrict: 'E',
+ terminal: true,
+ compile: function(element, attr) {
+ if (attr.type == 'text/ng-template') {
+ var templateUrl = attr.id;
+ $templateCache.put(templateUrl, element.text());
+ }
+ }
+ };
+}];
diff --git a/src/widget/select.js b/src/directive/select.js
index 5ed1367f..5ed1367f 100644
--- a/src/widget/select.js
+++ b/src/directive/select.js
diff --git a/src/directive/style.js b/src/directive/style.js
new file mode 100644
index 00000000..68ea1465
--- /dev/null
+++ b/src/directive/style.js
@@ -0,0 +1,6 @@
+'use strict';
+
+var styleDirective = valueFn({
+ restrict: 'E',
+ terminal: true
+});
diff --git a/src/directives.js b/src/directives.js
deleted file mode 100644
index 9b231850..00000000
--- a/src/directives.js
+++ /dev/null
@@ -1,1031 +0,0 @@
-'use strict';
-
-function ngDirective(directive) {
- if (isFunction(directive)) {
- directive = {
- link: directive
- }
- }
- directive.restrict = directive.restrict || 'AC';
- return valueFn(directive);
-};
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:init
- *
- * @description
- * The `ng:init` attribute specifies initialization tasks to be executed
- * before the template enters execution mode during bootstrap.
- *
- * @element ANY
- * @param {expression} expression {@link guide/dev_guide.expressions Expression} to eval.
- *
- * @example
- <doc:example>
- <doc:source>
- <div ng:init="greeting='Hello'; person='World'">
- {{greeting}} {{person}}!
- </div>
- </doc:source>
- <doc:scenario>
- it('should check greeting', function() {
- expect(binding('greeting')).toBe('Hello');
- expect(binding('person')).toBe('World');
- });
- </doc:scenario>
- </doc:example>
- */
-var ngInitDirective = ngDirective({
- compile: function() {
- return {
- pre: function(scope, element, attrs) {
- scope.$eval(attrs.ngInit);
- }
- }
- }
-});
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:controller
- *
- * @description
- * The `ng:controller` directive assigns behavior to a scope. This is a key aspect of how angular
- * supports the principles behind the Model-View-Controller design pattern.
- *
- * MVC components in angular:
- *
- * * Model — The Model is data in scope properties; scopes are attached to the DOM.
- * * View — The template (HTML with data bindings) is rendered into the View.
- * * Controller — The `ng:controller` directive specifies a Controller class; the class has
- * methods that typically express the business logic behind the application.
- *
- * Note that an alternative way to define controllers is via the `{@link angular.module.ng.$route}`
- * service.
- *
- * @element ANY
- * @scope
- * @param {expression} expression Name of a globally accessible constructor function or an
- * {@link guide/dev_guide.expressions expression} that on the current scope evaluates to a
- * constructor function.
- *
- * @example
- * Here is a simple form for editing user contact information. Adding, removing, clearing, and
- * greeting are methods declared on the controller (see source tab). These methods can
- * easily be called from the angular markup. Notice that the scope becomes the `this` for the
- * controller's instance. This allows for easy access to the view data from the controller. Also
- * notice that any changes to the data are automatically reflected in the View without the need
- * for a manual update.
- <doc:example>
- <doc:source>
- <script type="text/javascript">
- function SettingsController($scope) {
- $scope.name = "John Smith";
- $scope.contacts = [
- {type:'phone', value:'408 555 1212'},
- {type:'email', value:'john.smith@example.org'} ];
-
- $scope.greet = function() {
- alert(this.name);
- };
-
- $scope.addContact = function() {
- this.contacts.push({type:'email', value:'yourname@example.org'});
- };
-
- $scope.removeContact = function(contactToRemove) {
- var index = this.contacts.indexOf(contactToRemove);
- this.contacts.splice(index, 1);
- };
-
- $scope.clearContact = function(contact) {
- contact.type = 'phone';
- contact.value = '';
- };
- }
- </script>
- <div ng:controller="SettingsController">
- Name: <input type="text" ng:model="name"/>
- [ <a href="" ng:click="greet()">greet</a> ]<br/>
- Contact:
- <ul>
- <li ng:repeat="contact in contacts">
- <select ng:model="contact.type">
- <option>phone</option>
- <option>email</option>
- </select>
- <input type="text" ng:model="contact.value"/>
- [ <a href="" ng:click="clearContact(contact)">clear</a>
- | <a href="" ng:click="removeContact(contact)">X</a> ]
- </li>
- <li>[ <a href="" ng:click="addContact()">add</a> ]</li>
- </ul>
- </div>
- </doc:source>
- <doc:scenario>
- it('should check controller', function() {
- expect(element('.doc-example-live div>:input').val()).toBe('John Smith');
- expect(element('.doc-example-live li:nth-child(1) input').val())
- .toBe('408 555 1212');
- expect(element('.doc-example-live li:nth-child(2) input').val())
- .toBe('john.smith@example.org');
-
- element('.doc-example-live li:first a:contains("clear")').click();
- expect(element('.doc-example-live li:first input').val()).toBe('');
-
- element('.doc-example-live li:last a:contains("add")').click();
- expect(element('.doc-example-live li:nth-child(3) input').val())
- .toBe('yourname@example.org');
- });
- </doc:scenario>
- </doc:example>
- */
-var ngControllerDirective = ['$controller', '$window', function($controller, $window) {
- return {
- scope: true,
- controller: '@'
- }
-}];
-
-
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:bind
- *
- * @description
- * The `ng:bind` attribute tells Angular to replace the text content of the specified HTML element
- * with the value of a given expression, and to update the text content when the value of that
- * expression changes.
- *
- * Typically, you don't use `ng:bind` directly, but instead you use the double curly markup like
- * `{{ expression }}` and let the Angular compiler transform it to
- * `<span ng:bind="expression"></span>` when the template is compiled.
- *
- * @element ANY
- * @param {expression} expression {@link guide/dev_guide.expressions Expression} to evaluate.
- *
- * @example
- * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
- <doc:example>
- <doc:source>
- <script>
- function Ctrl($scope) {
- $scope.name = 'Whirled';
- }
- </script>
- <div ng:controller="Ctrl">
- Enter name: <input type="text" ng:model="name"> <br/>
- Hello <span ng:bind="name"></span>!
- </div>
- </doc:source>
- <doc:scenario>
- it('should check ng:bind', function() {
- expect(using('.doc-example-live').binding('name')).toBe('Whirled');
- using('.doc-example-live').input('name').enter('world');
- expect(using('.doc-example-live').binding('name')).toBe('world');
- });
- </doc:scenario>
- </doc:example>
- */
-var ngBindDirective = ngDirective(function(scope, element, attr) {
- element.addClass('ng-binding').data('$binding', attr.ngBind);
- scope.$watch(attr.ngBind, function(value) {
- element.text(value == undefined ? '' : value);
- });
-});
-
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:bind-html-unsafe
- *
- * @description
- * Creates a binding that will innerHTML the result of evaluating the `expression` into the current
- * element. *The innerHTML-ed content will not be sanitized!* You should use this directive only if
- * {@link angular.module.ng.$compileProvider.directive.ng:bind-html ng:bind-html} directive is too
- * restrictive and when you absolutely trust the source of the content you are binding to.
- *
- * See {@link angular.module.ng.$sanitize $sanitize} docs for examples.
- *
- * @element ANY
- * @param {expression} expression {@link guide/dev_guide.expressions Expression} to evaluate.
- */
-var ngBindHtmlUnsafeDirective = ngDirective(function(scope, element, attr) {
- element.addClass('ng-binding').data('$binding', attr.ngBindHtmlUnsafe);
- scope.$watch(attr.ngBindHtmlUnsafe, function(value) {
- element.html(value == undefined ? '' : value);
- });
-});
-
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:bind-html
- *
- * @description
- * Creates a binding that will sanitize the result of evaluating the `expression` with the
- * {@link angular.module.ng.$sanitize $sanitize} service and innerHTML the result into the current
- * element.
- *
- * See {@link angular.module.ng.$sanitize $sanitize} docs for examples.
- *
- * @element ANY
- * @param {expression} expression {@link guide/dev_guide.expressions Expression} to evaluate.
- */
-var ngBindHtmlDirective = ['$sanitize', function($sanitize) {
- return function(scope, element, attr) {
- element.addClass('ng-binding').data('$binding', attr.ngBindHtml);
- scope.$watch(attr.ngBindHtml, function(value) {
- if (value = $sanitize(value)) {
- element.html(value);
- }
- });
- }
-}];
-
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:bind-template
- *
- * @description
- * The `ng:bind-template` attribute specifies that the element
- * text should be replaced with the template in ng:bind-template.
- * Unlike ng:bind the ng:bind-template can contain multiple `{{` `}}`
- * expressions. (This is required since some HTML elements
- * can not have SPAN elements such as TITLE, or OPTION to name a few.)
- *
- * @element ANY
- * @param {string} template of form
- * <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
- *
- * @example
- * Try it here: enter text in text box and watch the greeting change.
- <doc:example>
- <doc:source>
- <script>
- function Ctrl($scope) {
- $scope.salutation = 'Hello';
- $scope.name = 'World';
- }
- </script>
- <div ng:controller="Ctrl">
- Salutation: <input type="text" ng:model="salutation"><br/>
- Name: <input type="text" ng:model="name"><br/>
- <pre ng:bind-template="{{salutation}} {{name}}!"></pre>
- </div>
- </doc:source>
- <doc:scenario>
- it('should check ng:bind', function() {
- expect(using('.doc-example-live').binding('salutation')).
- toBe('Hello');
- expect(using('.doc-example-live').binding('name')).
- toBe('World');
- using('.doc-example-live').input('salutation').enter('Greetings');
- using('.doc-example-live').input('name').enter('user');
- expect(using('.doc-example-live').binding('salutation')).
- toBe('Greetings');
- expect(using('.doc-example-live').binding('name')).
- toBe('user');
- });
- </doc:scenario>
- </doc:example>
- */
-var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
- return function(scope, element, attr) {
- // TODO: move this to scenario runner
- var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
- element.addClass('ng-binding').data('$binding', interpolateFn);
- attr.$observe('ngBindTemplate', function(value) {
- element.text(value);
- });
- }
-}];
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:bind-attr
- *
- * @description
- * The `ng:bind-attr` attribute specifies that a
- * {@link guide/dev_guide.templates.databinding databinding} should be created between a particular
- * element attribute and a given expression. Unlike `ng:bind`, the `ng:bind-attr` contains one or
- * more JSON key value pairs; each pair specifies an attribute and the
- * {@link guide/dev_guide.expressions expression} to which it will be mapped.
- *
- * Instead of writing `ng:bind-attr` statements in your HTML, you can use double-curly markup to
- * specify an <tt ng:non-bindable>{{expression}}</tt> for the value of an attribute.
- * At compile time, the attribute is translated into an
- * `<span ng:bind-attr="{attr:expression}"></span>`.
- *
- * The following HTML snippet shows how to specify `ng:bind-attr`:
- * <pre>
- * <a ng:bind-attr='{"href":"http://www.google.com/search?q={{query}}"}'>Google</a>
- * </pre>
- *
- * This is cumbersome, so as we mentioned using double-curly markup is a prefered way of creating
- * this binding:
- * <pre>
- * <a href="http://www.google.com/search?q={{query}}">Google</a>
- * </pre>
- *
- * During compilation, the template with attribute markup gets translated to the ng:bind-attr form
- * mentioned above.
- *
- * _Note_: You might want to consider using {@link angular.module.ng.$compileProvider.directive.ng:href ng:href} instead of
- * `href` if the binding is present in the main application template (`index.html`) and you want to
- * make sure that a user is not capable of clicking on raw/uncompiled link.
- *
- *
- * @element ANY
- * @param {string} attribute_json one or more JSON key-value pairs representing
- * the attributes to replace with expressions. Each key matches an attribute
- * which needs to be replaced. Each value is a text template of
- * the attribute with the embedded
- * <tt ng:non-bindable>{{expression}}</tt>s. Any number of
- * key-value pairs can be specified.
- *
- * @example
- * Enter a search string in the Live Preview text box and then click "Google". The search executes instantly.
- <doc:example>
- <doc:source>
- <script>
- function Ctrl($scope) {
- $scope.query = 'AngularJS';
- }
- </script>
- <div ng:controller="Ctrl">
- Google for:
- <input type="text" ng:model="query"/>
- <a ng:bind-attr='{"href":"http://www.google.com/search?q={{query}}"}'>
- Google
- </a> (ng:bind-attr) |
- <a href="http://www.google.com/search?q={{query}}">Google</a>
- (curly binding in attribute val)
- </div>
- </doc:source>
- <doc:scenario>
- it('should check ng:bind-attr', function() {
- expect(using('.doc-example-live').element('a').attr('href')).
- toBe('http://www.google.com/search?q=AngularJS');
- using('.doc-example-live').input('query').enter('google');
- expect(using('.doc-example-live').element('a').attr('href')).
- toBe('http://www.google.com/search?q=google');
- });
- </doc:scenario>
- </doc:example>
- */
-
-var ngBindAttrDirective = ['$interpolate', function($interpolate) {
- return function(scope, element, attr) {
- var lastValue = {};
- var interpolateFns = {};
- scope.$watch(function() {
- var values = scope.$eval(attr.ngBindAttr);
- for(var key in values) {
- var exp = values[key],
- fn = (interpolateFns[exp] ||
- (interpolateFns[values[key]] = $interpolate(exp))),
- value = fn(scope);
- if (lastValue[key] !== value) {
- attr.$set(key, lastValue[key] = value);
- }
- }
- });
- }
-}];
-
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:click
- *
- * @description
- * The ng:click allows you to specify custom behavior when
- * element is clicked.
- *
- * @element ANY
- * @param {expression} expression {@link guide/dev_guide.expressions Expression} to evaluate upon
- * click. (Event object is available as `$event`)
- *
- * @example
- <doc:example>
- <doc:source>
- <button ng:click="count = count + 1" ng:init="count=0">
- Increment
- </button>
- count: {{count}}
- </doc:source>
- <doc:scenario>
- it('should check ng:click', function() {
- expect(binding('count')).toBe('0');
- element('.doc-example-live :button').click();
- expect(binding('count')).toBe('1');
- });
- </doc:scenario>
- </doc:example>
- */
-/*
- * A directive that allows creation of custom onclick handlers that are defined as angular
- * expressions and are compiled and executed within the current scope.
- *
- * Events that are handled via these handler are always configured not to propagate further.
- */
-var ngEventDirectives = {};
-forEach(
- 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave'.split(' '),
- function(name) {
- var directiveName = directiveNormalize('ng-' + name);
- ngEventDirectives[directiveName] = ['$parse', function($parse) {
- return function(scope, element, attr) {
- var fn = $parse(attr[directiveName]);
- element.bind(lowercase(name), function(event) {
- scope.$apply(function() {
- fn(scope, {$event:event});
- });
- });
- };
- }];
- }
-);
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:dblclick
- *
- * @description
- * The ng:dblclick allows you to specify custom behavior on dblclick event.
- *
- * @element ANY
- * @param {expression} expression {@link guide/dev_guide.expressions Expression} to evaluate upon
- * dblclick. (Event object is available as `$event`)
- *
- * @example
- * See {@link angular.module.ng.$compileProvider.directive.ng:click ng:click}
- */
-
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:mousedown
- *
- * @description
- * The ng:mousedown allows you to specify custom behavior on mousedown event.
- *
- * @element ANY
- * @param {expression} expression {@link guide/dev_guide.expressions Expression} to evaluate upon
- * mousedown. (Event object is available as `$event`)
- *
- * @example
- * See {@link angular.module.ng.$compileProvider.directive.ng:click ng:click}
- */
-
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:mouseup
- *
- * @description
- * Specify custom behavior on mouseup event.
- *
- * @element ANY
- * @param {expression} expression {@link guide/dev_guide.expressions Expression} to evaluate upon
- * mouseup. (Event object is available as `$event`)
- *
- * @example
- * See {@link angular.module.ng.$compileProvider.directive.ng:click ng:click}
- */
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:mouseover
- *
- * @description
- * Specify custom behavior on mouseover event.
- *
- * @element ANY
- * @param {expression} expression {@link guide/dev_guide.expressions Expression} to evaluate upon
- * mouseover. (Event object is available as `$event`)
- *
- * @example
- * See {@link angular.module.ng.$compileProvider.directive.ng:click ng:click}
- */
-
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:mouseenter
- *
- * @description
- * Specify custom behavior on mouseenter event.
- *
- * @element ANY
- * @param {expression} expression {@link guide/dev_guide.expressions Expression} to evaluate upon
- * mouseenter. (Event object is available as `$event`)
- *
- * @example
- * See {@link angular.module.ng.$compileProvider.directive.ng:click ng:click}
- */
-
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:mouseleave
- *
- * @description
- * Specify custom behavior on mouseleave event.
- *
- * @element ANY
- * @param {expression} expression {@link guide/dev_guide.expressions Expression} to evaluate upon
- * mouseleave. (Event object is available as `$event`)
- *
- * @example
- * See {@link angular.module.ng.$compileProvider.directive.ng:click ng:click}
- */
-
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:mousemove
- *
- * @description
- * Specify custom behavior on mousemove event.
- *
- * @element ANY
- * @param {expression} expression {@link guide/dev_guide.expressions Expression} to evaluate upon
- * mousemove. (Event object is available as `$event`)
- *
- * @example
- * See {@link angular.module.ng.$compileProvider.directive.ng:click ng:click}
- */
-
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:submit
- *
- * @description
- * Enables binding angular expressions to onsubmit events.
- *
- * Additionally it prevents the default action (which for form means sending the request to the
- * server and reloading the current page).
- *
- * @element form
- * @param {expression} expression {@link guide/dev_guide.expressions Expression} to eval.
- *
- * @example
- <doc:example>
- <doc:source>
- <script>
- function Ctrl($scope) {
- $scope.list = [];
- $scope.text = 'hello';
- $scope.submit = function() {
- if (this.text) {
- this.list.push(this.text);
- this.text = '';
- }
- };
- }
- </script>
- <form ng:submit="submit()" ng:controller="Ctrl">
- Enter text and hit enter:
- <input type="text" ng:model="text" name="text" />
- <input type="submit" id="submit" value="Submit" />
- <pre>list={{list}}</pre>
- </form>
- </doc:source>
- <doc:scenario>
- it('should check ng:submit', function() {
- expect(binding('list')).toBe('[]');
- element('.doc-example-live #submit').click();
- expect(binding('list')).toBe('["hello"]');
- expect(input('text').val()).toBe('');
- });
- it('should ignore empty strings', function() {
- expect(binding('list')).toBe('[]');
- element('.doc-example-live #submit').click();
- element('.doc-example-live #submit').click();
- expect(binding('list')).toBe('["hello"]');
- });
- </doc:scenario>
- </doc:example>
- */
-var ngSubmitDirective = ngDirective(function(scope, element, attrs) {
- element.bind('submit', function() {
- scope.$apply(attrs.ngSubmit);
- });
-});
-
-
-function classDirective(name, selector) {
- name = 'ngClass' + name;
- return ngDirective(function(scope, element, attr) {
- scope.$watch(attr[name], function(newVal, oldVal) {
- if (selector === true || scope.$index % 2 === selector) {
- if (oldVal && (newVal !== oldVal)) {
- if (isObject(oldVal) && !isArray(oldVal))
- oldVal = map(oldVal, function(v, k) { if (v) return k });
- element.removeClass(isArray(oldVal) ? oldVal.join(' ') : oldVal);
- }
- if (isObject(newVal) && !isArray(newVal))
- newVal = map(newVal, function(v, k) { if (v) return k });
- if (newVal) element.addClass(isArray(newVal) ? newVal.join(' ') : newVal); }
- }, true);
- });
-}
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:class
- *
- * @description
- * The `ng:class` allows you to set CSS class on HTML element dynamically by databinding an
- * expression that represents all classes to be added.
- *
- * The directive won't add duplicate classes if a particular class was already set.
- *
- * When the expression changes, the previously added classes are removed and only then the classes
- * new classes are added.
- *
- * @element ANY
- * @param {expression} expression {@link guide/dev_guide.expressions Expression} to eval. The result
- * of the evaluation can be a string representing space delimited class
- * names, an array, or a map of class names to boolean values.
- *
- * @example
- <doc:example>
- <doc:source>
- <input type="button" value="set" ng:click="myVar='ng-invalid'">
- <input type="button" value="clear" ng:click="myVar=''">
- <br>
- <span ng:class="myVar">Sample Text &nbsp;&nbsp;&nbsp;&nbsp;</span>
- </doc:source>
- <doc:scenario>
- it('should check ng:class', function() {
- expect(element('.doc-example-live span').prop('className')).not().
- toMatch(/ng-invalid/);
-
- using('.doc-example-live').element(':button:first').click();
-
- expect(element('.doc-example-live span').prop('className')).
- toMatch(/ng-invalid/);
-
- using('.doc-example-live').element(':button:last').click();
-
- expect(element('.doc-example-live span').prop('className')).not().
- toMatch(/ng-invalid/);
- });
- </doc:scenario>
- </doc:example>
- */
-var ngClassDirective = classDirective('', true);
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:class-odd
- *
- * @description
- * The `ng:class-odd` and `ng:class-even` works exactly as
- * {@link angular.module.ng.$compileProvider.directive.ng:class ng:class}, except it works in conjunction with `ng:repeat` and
- * takes affect only on odd (even) rows.
- *
- * This directive can be applied only within a scope of an
- * {@link angular.module.ng.$compileProvider.directive.ng:repeat ng:repeat}.
- *
- * @element ANY
- * @param {expression} expression {@link guide/dev_guide.expressions Expression} to eval. The result
- * of the evaluation can be a string representing space delimited class names or an array.
- *
- * @example
- <doc:example>
- <doc:source>
- <ol ng:init="names=['John', 'Mary', 'Cate', 'Suz']">
- <li ng:repeat="name in names">
- <span ng:class-odd="'ng-format-negative'"
- ng:class-even="'ng-invalid'">
- {{name}} &nbsp; &nbsp; &nbsp;
- </span>
- </li>
- </ol>
- </doc:source>
- <doc:scenario>
- it('should check ng:class-odd and ng:class-even', function() {
- expect(element('.doc-example-live li:first span').prop('className')).
- toMatch(/ng-format-negative/);
- expect(element('.doc-example-live li:last span').prop('className')).
- toMatch(/ng-invalid/);
- });
- </doc:scenario>
- </doc:example>
- */
-var ngClassOddDirective = classDirective('Odd', 0);
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:class-even
- *
- * @description
- * The `ng:class-odd` and `ng:class-even` works exactly as
- * {@link angular.module.ng.$compileProvider.directive.ng:class ng:class}, except it works in conjunction with `ng:repeat` and
- * takes affect only on odd (even) rows.
- *
- * This directive can be applied only within a scope of an
- * {@link angular.module.ng.$compileProvider.directive.ng:repeat ng:repeat}.
- *
- * @element ANY
- * @param {expression} expression {@link guide/dev_guide.expressions Expression} to eval. The result
- * of the evaluation can be a string representing space delimited class names or an array.
- *
- * @example
- <doc:example>
- <doc:source>
- <ol ng:init="names=['John', 'Mary', 'Cate', 'Suz']">
- <li ng:repeat="name in names">
- <span ng:class-odd="'odd'" ng:class-even="'even'">
- {{name}} &nbsp; &nbsp; &nbsp;
- </span>
- </li>
- </ol>
- </doc:source>
- <doc:scenario>
- it('should check ng:class-odd and ng:class-even', function() {
- expect(element('.doc-example-live li:first span').prop('className')).
- toMatch(/odd/);
- expect(element('.doc-example-live li:last span').prop('className')).
- toMatch(/even/);
- });
- </doc:scenario>
- </doc:example>
- */
-var ngClassEvenDirective = classDirective('Even', 1);
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:show
- *
- * @description
- * The `ng:show` and `ng:hide` directives show or hide a portion of the DOM tree (HTML)
- * conditionally.
- *
- * @element ANY
- * @param {expression} expression If the {@link guide/dev_guide.expressions expression} is truthy
- * then the element is shown or hidden respectively.
- *
- * @example
- <doc:example>
- <doc:source>
- Click me: <input type="checkbox" ng:model="checked"><br/>
- Show: <span ng:show="checked">I show up when your checkbox is checked.</span> <br/>
- Hide: <span ng:hide="checked">I hide when your checkbox is checked.</span>
- </doc:source>
- <doc:scenario>
- it('should check ng:show / ng:hide', function() {
- expect(element('.doc-example-live span:first:hidden').count()).toEqual(1);
- expect(element('.doc-example-live span:last:visible').count()).toEqual(1);
-
- input('checked').check();
-
- expect(element('.doc-example-live span:first:visible').count()).toEqual(1);
- expect(element('.doc-example-live span:last:hidden').count()).toEqual(1);
- });
- </doc:scenario>
- </doc:example>
- */
-//TODO(misko): refactor to remove element from the DOM
-var ngShowDirective = ngDirective(function(scope, element, attr){
- scope.$watch(attr.ngShow, function(value){
- element.css('display', toBoolean(value) ? '' : 'none');
- });
-});
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:hide
- *
- * @description
- * The `ng:hide` and `ng:show` directives hide or show a portion
- * of the HTML conditionally.
- *
- * @element ANY
- * @param {expression} expression If the {@link guide/dev_guide.expressions expression} truthy then
- * the element is shown or hidden respectively.
- *
- * @example
- <doc:example>
- <doc:source>
- Click me: <input type="checkbox" ng:model="checked"><br/>
- Show: <span ng:show="checked">I show up when you checkbox is checked?</span> <br/>
- Hide: <span ng:hide="checked">I hide when you checkbox is checked?</span>
- </doc:source>
- <doc:scenario>
- it('should check ng:show / ng:hide', function() {
- expect(element('.doc-example-live span:first:hidden').count()).toEqual(1);
- expect(element('.doc-example-live span:last:visible').count()).toEqual(1);
-
- input('checked').check();
-
- expect(element('.doc-example-live span:first:visible').count()).toEqual(1);
- expect(element('.doc-example-live span:last:hidden').count()).toEqual(1);
- });
- </doc:scenario>
- </doc:example>
- */
-//TODO(misko): refactor to remove element from the DOM
-var ngHideDirective = ngDirective(function(scope, element, attr){
- scope.$watch(attr.ngHide, function(value){
- element.css('display', toBoolean(value) ? 'none' : '');
- });
-});
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:style
- *
- * @description
- * The ng:style allows you to set CSS style on an HTML element conditionally.
- *
- * @element ANY
- * @param {expression} expression {@link guide/dev_guide.expressions Expression} which evals to an
- * object whose keys are CSS style names and values are corresponding values for those CSS
- * keys.
- *
- * @example
- <doc:example>
- <doc:source>
- <input type="button" value="set" ng:click="myStyle={color:'red'}">
- <input type="button" value="clear" ng:click="myStyle={}">
- <br/>
- <span ng:style="myStyle">Sample Text</span>
- <pre>myStyle={{myStyle}}</pre>
- </doc:source>
- <doc:scenario>
- it('should check ng:style', function() {
- expect(element('.doc-example-live span').css('color')).toBe('rgb(0, 0, 0)');
- element('.doc-example-live :button[value=set]').click();
- expect(element('.doc-example-live span').css('color')).toBe('rgb(255, 0, 0)');
- element('.doc-example-live :button[value=clear]').click();
- expect(element('.doc-example-live span').css('color')).toBe('rgb(0, 0, 0)');
- });
- </doc:scenario>
- </doc:example>
- */
-var ngStyleDirective = ngDirective(function(scope, element, attr) {
- scope.$watch(attr.ngStyle, function(newStyles, oldStyles) {
- if (oldStyles && (newStyles !== oldStyles)) {
- forEach(oldStyles, function(val, style) { element.css(style, '');});
- }
- if (newStyles) element.css(newStyles);
- }, true);
-});
-
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:cloak
- *
- * @description
- * The `ng:cloak` directive is used to prevent the Angular html template from being briefly
- * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
- * directive to avoid the undesirable flicker effect caused by the html template display.
- *
- * The directive can be applied to the `<body>` element, but typically a fine-grained application is
- * prefered in order to benefit from progressive rendering of the browser view.
- *
- * `ng:cloak` works in cooperation with a css rule that is embedded within `angular.js` and
- * `angular.min.js` files. Following is the css rule:
- *
- * <pre>
- * [ng\:cloak], .ng-cloak {
- * display: none;
- * }
- * </pre>
- *
- * When this css rule is loaded by the browser, all html elements (including their children) that
- * are tagged with the `ng:cloak` directive are hidden. When Angular comes across this directive
- * during the compilation of the template it deletes the `ng:cloak` element attribute, which
- * makes the compiled element visible.
- *
- * For the best result, `angular.js` script must be loaded in the head section of the html file;
- * alternatively, the css rule (above) must be included in the external stylesheet of the
- * application.
- *
- * Legacy browsers, like IE7, do not provide attribute selector support (added in CSS 2.1) so they
- * cannot match the `[ng\:cloak]` selector. To work around this limitation, you must add the css
- * class `ng-cloak` in addition to `ng:cloak` directive as shown in the example below.
- *
- * @element ANY
- *
- * @example
- <doc:example>
- <doc:source>
- <div id="template1" ng:cloak>{{ 'hello' }}</div>
- <div id="template2" ng:cloak class="ng-cloak">{{ 'hello IE7' }}</div>
- </doc:source>
- <doc:scenario>
- it('should remove the template directive and css class', function() {
- expect(element('.doc-example-live #template1').attr('ng:cloak')).
- not().toBeDefined();
- expect(element('.doc-example-live #template2').attr('ng:cloak')).
- not().toBeDefined();
- });
- </doc:scenario>
- </doc:example>
- *
- */
-var ngCloakDirective = ngDirective({
- compile: function(element, attr) {
- attr.$set('ngCloak', undefined);
- element.removeClass('ng-cloak');
- }
-});
-
-function ngAttributeAliasDirective(propName, attrName) {
- ngAttributeAliasDirectives[directiveNormalize('ng-' + attrName)] = valueFn(
- function(scope, element, attr) {
- attr.$observe(directiveNormalize('ng-' + attrName), function(value) {
- attr.$set(attrName, value);
- });
- }
- );
-}
-var ngAttributeAliasDirectives = {};
-forEach(BOOLEAN_ATTR, ngAttributeAliasDirective);
-ngAttributeAliasDirective(null, 'src');
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:transclude
- *
- * @description
- * Insert the transcluded DOM here.
- *
- * @element ANY
- *
- * @example
- <doc:example module="transclude">
- <doc:source>
- <script>
- function Ctrl($scope) {
- $scope.title = 'Lorem Ipsum';
- $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
- }
-
- angular.module('transclude', [])
- .directive('pane', function(){
- return {
- restrict: 'E',
- transclude: true,
- scope: 'isolate',
- locals: { title:'bind' },
- template: '<div style="border: 1px solid black;">' +
- '<div style="background-color: gray">{{title}}</div>' +
- '<div ng-transclude></div>' +
- '</div>'
- };
- });
- </script>
- <div ng:controller="Ctrl">
- <input ng:model="title"><br>
- <textarea ng:model="text"></textarea> <br/>
- <pane title="{{title}}">{{text}}</pane>
- </div>
- </doc:source>
- <doc:scenario>
- it('should have transcluded', function() {
- input('title').enter('TITLE');
- input('text').enter('TEXT');
- expect(binding('title')).toEqual('TITLE');
- expect(binding('text')).toEqual('TEXT');
- });
- </doc:scenario>
- </doc:example>
- *
- */
-var ngTranscludeDirective = ngDirective({
- controller: ['$transclude', '$element', function($transclude, $element) {
- $transclude(function(clone) {
- $element.append(clone);
- });
- }]
-});
-
-
-var styleDirective = valueFn({
- restrict: 'E',
- terminal: true
-});
-
-
-var onloadDirective = valueFn({
- restrict: 'AC',
- link: function(scope, elm, attr) {
- var onloadExp = attr.onload || ''; //workaround for jquery bug #7537)
-
- scope.$on('$contentLoaded', function(event) {
- scope.$eval(onloadExp);
- });
- }
-});
diff --git a/src/widgets.js b/src/widgets.js
index 235fbc2b..e69de29b 100644
--- a/src/widgets.js
+++ b/src/widgets.js
@@ -1,876 +0,0 @@
-'use strict';
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:include
- * @restrict EA
- *
- * @description
- * Fetches, compiles and includes an external HTML fragment.
- *
- * Keep in mind that Same Origin Policy applies to included resources
- * (e.g. ng:include won't work for file:// access).
- *
- * @scope
- *
- * @param {string} src angular expression evaluating to URL. If the source is a string constant,
- * make sure you wrap it in quotes, e.g. `src="'myPartialTemplate.html'"`.
- * @param {Scope=} [scope=new_child_scope] optional expression which evaluates to an
- * instance of angular.module.ng.$rootScope.Scope to set the HTML fragment to.
- * @param {string=} onload Expression to evaluate when a new partial is loaded.
- *
- * @param {string=} autoscroll Whether `ng:include` should call {@link angular.module.ng.$anchorScroll
- * $anchorScroll} to scroll the viewport after the content is loaded.
- *
- * - If the attribute is not set, disable scrolling.
- * - If the attribute is set without value, enable scrolling.
- * - Otherwise enable scrolling only if the expression evaluates to truthy value.
- *
- * @example
- <doc:example>
- <doc:source jsfiddle="false">
- <script>
- function Ctrl($scope) {
- $scope.templates =
- [ { name: 'template1.html', url: 'examples/ng-include/template1.html'}
- , { name: 'template2.html', url: 'examples/ng-include/template2.html'} ];
- $scope.template = $scope.templates[0];
- }
- </script>
- <div ng:controller="Ctrl">
- <select ng:model="template" ng:options="t.name for t in templates">
- <option value="">(blank)</option>
- </select>
- url of the template: <tt><a href="{{template.url}}">{{template.url}}</a></tt>
- <hr/>
- <div ng-include src="template.url"></div>
- </div>
- </doc:source>
- <doc:scenario>
- it('should load template1.html', function() {
- expect(element('.doc-example-live [ng-include]').text()).
- toBe('Content of template1.html\n');
- });
- it('should load template2.html', function() {
- select('template').option('1');
- expect(element('.doc-example-live [ng-include]').text()).
- toBe('Content of template2.html\n');
- });
- it('should change to blank', function() {
- select('template').option('');
- expect(element('.doc-example-live [ng-include]').text()).toEqual('');
- });
- </doc:scenario>
- </doc:example>
- */
-var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile',
- function($http, $templateCache, $anchorScroll, $compile) {
- return {
- restrict: 'EA',
- compile: function(element, attr) {
- var srcExp = attr.src,
- scopeExp = attr.scope || '',
- autoScrollExp = attr.autoscroll;
-
- return function(scope, element, attr) {
- var changeCounter = 0,
- childScope;
-
- function incrementChange() { changeCounter++;}
- scope.$watch(srcExp, incrementChange);
- scope.$watch(function() {
- var includeScope = scope.$eval(scopeExp);
- if (includeScope) return includeScope.$id;
- }, incrementChange);
- scope.$watch(function() {return changeCounter;}, function(newChangeCounter) {
- var src = scope.$eval(srcExp),
- useScope = scope.$eval(scopeExp);
-
- function clearContent() {
- // if this callback is still desired
- if (newChangeCounter === changeCounter) {
- if (childScope) childScope.$destroy();
- childScope = null;
- element.html('');
- }
- }
-
- if (src) {
- $http.get(src, {cache: $templateCache}).success(function(response) {
- // if this callback is still desired
- if (newChangeCounter === changeCounter) {
- element.html(response);
- if (childScope) childScope.$destroy();
- childScope = useScope ? useScope : scope.$new();
- $compile(element.contents())(childScope);
- if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
- $anchorScroll();
- }
- scope.$emit('$contentLoaded');
- }
- }).error(clearContent);
- } else {
- clearContent();
- }
- });
- };
- }
- }
-}];
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:switch
- * @restrict EA
- *
- * @description
- * Conditionally change the DOM structure.
- *
- * @usageContent
- * <any ng:switch-when="matchValue1">...</any>
- * <any ng:switch-when="matchValue2">...</any>
- * ...
- * <any ng:switch-default>...</any>
- *
- * @scope
- * @param {*} on expression to match against <tt>ng:switch-when</tt>.
- * @paramDescription
- * On child elments add:
- *
- * * `ng:switch-when`: the case statement to match against. If match then this
- * case will be displayed.
- * * `ng:switch-default`: the default case when no other casses match.
- *
- * @example
- <doc:example>
- <doc:source>
- <script>
- function Ctrl($scope) {
- $scope.items = ['settings', 'home', 'other'];
- $scope.selection = $scope.items[0];
- }
- </script>
- <div ng:controller="Ctrl">
- <select ng:model="selection" ng:options="item for item in items">
- </select>
- <tt>selection={{selection}}</tt>
- <hr/>
- <ng:switch on="selection" >
- <div ng:switch-when="settings">Settings Div</div>
- <span ng:switch-when="home">Home Span</span>
- <span ng:switch-default>default</span>
- </ng:switch>
- </div>
- </doc:source>
- <doc:scenario>
- it('should start in settings', function() {
- expect(element('.doc-example-live ng\\:switch').text()).toMatch(/Settings Div/);
- });
- it('should change to home', function() {
- select('selection').option('home');
- expect(element('.doc-example-live ng\\:switch').text()).toMatch(/Home Span/);
- });
- it('should select deafault', function() {
- select('selection').option('other');
- expect(element('.doc-example-live ng\\:switch').text()).toMatch(/default/);
- });
- </doc:scenario>
- </doc:example>
- */
-var NG_SWITCH = 'ng-switch';
-var ngSwitchDirective = valueFn({
- restrict: 'EA',
- compile: function(element, attr) {
- var watchExpr = attr.ngSwitch || attr.on,
- cases = {};
-
- element.data(NG_SWITCH, cases);
- return function(scope, element){
- var selectedTransclude,
- selectedElement;
-
- scope.$watch(watchExpr, function(value) {
- if (selectedElement) {
- selectedElement.remove();
- selectedElement = null;
- }
- if ((selectedTransclude = cases['!' + value] || cases['?'])) {
- scope.$eval(attr.change);
- selectedTransclude(scope.$new(), function(caseElement, scope) {
- selectedElement = caseElement;
- element.append(caseElement);
- element.bind('$destroy', bind(scope, scope.$destroy));
- });
- }
- });
- };
- }
-});
-
-var ngSwitchWhenDirective = ngDirective({
- transclude: 'element',
- priority: 500,
- compile: function(element, attrs, transclude) {
- var cases = element.inheritedData(NG_SWITCH);
- assertArg(cases);
- cases['!' + attrs.ngSwitchWhen] = transclude;
- }
-});
-
-var ngSwitchDefaultDirective = ngDirective({
- transclude: 'element',
- priority: 500,
- compile: function(element, attrs, transclude) {
- var cases = element.inheritedData(NG_SWITCH);
- assertArg(cases);
- cases['?'] = transclude;
- }
-});
-
-
-/*
- * Modifies the default behavior of html A tag, so that the default action is prevented when href
- * attribute is empty.
- *
- * The reasoning for this change is to allow easy creation of action links with ng:click without
- * changing the location or causing page reloads, e.g.:
- * <a href="" ng:click="model.$save()">Save</a>
- */
-var htmlAnchorDirective = valueFn({
- restrict: 'E',
- compile: function(element, attr) {
- // turn <a href ng:click="..">link</a> into a link in IE
- // but only if it doesn't have name attribute, in which case it's an anchor
- if (!attr.href) {
- attr.$set('href', '');
- }
-
- return function(scope, element) {
- element.bind('click', function(event){
- // if we have no href url, then don't navigate anywhere.
- if (!element.attr('href')) {
- event.preventDefault();
- }
- });
- }
- }
-});
-
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:repeat
- *
- * @description
- * The `ng:repeat` widget instantiates a template once per item from a collection. Each template
- * instance gets its own scope, where the given loop variable is set to the current collection item,
- * and `$index` is set to the item index or key.
- *
- * Special properties are exposed on the local scope of each template instance, including:
- *
- * * `$index` – `{number}` – iterator offset of the repeated element (0..length-1)
- * * `$position` – `{string}` – position of the repeated element in the iterator. One of:
- * * `'first'`,
- * * `'middle'`
- * * `'last'`
- *
- * Note: Although `ng:repeat` looks like a directive, it is actually an attribute widget.
- *
- * @element ANY
- * @scope
- * @priority 1000
- * @param {string} repeat_expression The expression indicating how to enumerate a collection. Two
- * formats are currently supported:
- *
- * * `variable in expression` – where variable is the user defined loop variable and `expression`
- * is a scope expression giving the collection to enumerate.
- *
- * For example: `track in cd.tracks`.
- *
- * * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
- * and `expression` is the scope expression giving the collection to enumerate.
- *
- * For example: `(name, age) in {'adam':10, 'amalie':12}`.
- *
- * @example
- * This example initializes the scope to a list of names and
- * then uses `ng:repeat` to display every person:
- <doc:example>
- <doc:source>
- <div ng:init="friends = [{name:'John', age:25}, {name:'Mary', age:28}]">
- I have {{friends.length}} friends. They are:
- <ul>
- <li ng:repeat="friend in friends">
- [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
- </li>
- </ul>
- </div>
- </doc:source>
- <doc:scenario>
- it('should check ng:repeat', function() {
- var r = using('.doc-example-live').repeater('ul li');
- expect(r.count()).toBe(2);
- expect(r.row(0)).toEqual(["1","John","25"]);
- expect(r.row(1)).toEqual(["2","Mary","28"]);
- });
- </doc:scenario>
- </doc:example>
- */
-var ngRepeatDirective = ngDirective({
- transclude: 'element',
- priority: 1000,
- terminal: true,
- compile: function(element, attr, linker) {
- return function(scope, iterStartElement, attr){
- var expression = attr.ngRepeat;
- var match = expression.match(/^\s*(.+)\s+in\s+(.*)\s*$/),
- lhs, rhs, valueIdent, keyIdent;
- if (! match) {
- throw Error("Expected ng:repeat in form of '_item_ in _collection_' but got '" +
- expression + "'.");
- }
- lhs = match[1];
- rhs = match[2];
- match = lhs.match(/^([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\)$/);
- if (!match) {
- throw Error("'item' in 'item in collection' should be identifier or (key, value) but got '" +
- keyValue + "'.");
- }
- valueIdent = match[3] || match[1];
- keyIdent = match[2];
-
- // Store a list of elements from previous run. This is a hash where key is the item from the
- // iterator, and the value is an array of objects with following properties.
- // - scope: bound scope
- // - element: previous element.
- // - index: position
- // We need an array of these objects since the same object can be returned from the iterator.
- // We expect this to be a rare case.
- var lastOrder = new HashQueueMap();
- scope.$watch(function(scope){
- var index, length,
- collection = scope.$eval(rhs),
- collectionLength = size(collection, true),
- childScope,
- // Same as lastOrder but it has the current state. It will become the
- // lastOrder on the next iteration.
- nextOrder = new HashQueueMap(),
- key, value, // key/value of iteration
- array, last, // last object information {scope, element, index}
- cursor = iterStartElement; // current position of the node
-
- if (!isArray(collection)) {
- // if object, extract keys, sort them and use to determine order of iteration over obj props
- array = [];
- for(key in collection) {
- if (collection.hasOwnProperty(key) && key.charAt(0) != '$') {
- array.push(key);
- }
- }
- array.sort();
- } else {
- array = collection || [];
- }
-
- // we are not using forEach for perf reasons (trying to avoid #call)
- for (index = 0, length = array.length; index < length; index++) {
- key = (collection === array) ? index : array[index];
- value = collection[key];
- last = lastOrder.shift(value);
- if (last) {
- // if we have already seen this object, then we need to reuse the
- // associated scope/element
- childScope = last.scope;
- nextOrder.push(value, last);
-
- if (index === last.index) {
- // do nothing
- cursor = last.element;
- } else {
- // existing item which got moved
- last.index = index;
- // This may be a noop, if the element is next, but I don't know of a good way to
- // figure this out, since it would require extra DOM access, so let's just hope that
- // the browsers realizes that it is noop, and treats it as such.
- cursor.after(last.element);
- cursor = last.element;
- }
- } else {
- // new item which we don't know about
- childScope = scope.$new();
- }
-
- childScope[valueIdent] = value;
- if (keyIdent) childScope[keyIdent] = key;
- childScope.$index = index;
- childScope.$position = index === 0 ?
- 'first' :
- (index == collectionLength - 1 ? 'last' : 'middle');
-
- if (!last) {
- linker(childScope, function(clone){
- cursor.after(clone);
- last = {
- scope: childScope,
- element: (cursor = clone),
- index: index
- };
- nextOrder.push(value, last);
- });
- }
- }
-
- //shrink children
- for (key in lastOrder) {
- if (lastOrder.hasOwnProperty(key)) {
- array = lastOrder[key];
- while(array.length) {
- value = array.pop();
- value.element.remove();
- value.scope.$destroy();
- }
- }
- }
-
- lastOrder = nextOrder;
- });
- };
- }
-});
-
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:non-bindable
- *
- * @description
- * Sometimes it is necessary to write code which looks like bindings but which should be left alone
- * by angular. Use `ng:non-bindable` to make angular ignore a chunk of HTML.
- *
- * Note: `ng:non-bindable` looks like a directive, but is actually an attribute widget.
- *
- * @element ANY
- *
- * @example
- * In this example there are two location where a simple binding (`{{}}`) is present, but the one
- * wrapped in `ng:non-bindable` is left alone.
- *
- * @example
- <doc:example>
- <doc:source>
- <div>Normal: {{1 + 2}}</div>
- <div ng:non-bindable>Ignored: {{1 + 2}}</div>
- </doc:source>
- <doc:scenario>
- it('should check ng:non-bindable', function() {
- expect(using('.doc-example-live').binding('1 + 2')).toBe('3');
- expect(using('.doc-example-live').element('div:last').text()).
- toMatch(/1 \+ 2/);
- });
- </doc:scenario>
- </doc:example>
- */
-var ngNonBindableDirective = ngDirective({ terminal: true });
-
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:view
- * @restrict ECA
- *
- * @description
- * # Overview
- * `ng:view` is a directive that complements the {@link angular.module.ng.$route $route} service by
- * including the rendered template of the current route into the main layout (`index.html`) file.
- * Every time the current route changes, the included view changes with it according to the
- * configuration of the `$route` service.
- *
- * @scope
- * @example
- <doc:example module="ngView">
- <doc:source>
- <script type="text/ng-template" id="examples/book.html">
- controller: {{name}}<br />
- Book Id: {{params.bookId}}<br />
- </script>
-
- <script type="text/ng-template" id="examples/chapter.html">
- controller: {{name}}<br />
- Book Id: {{params.bookId}}<br />
- Chapter Id: {{params.chapterId}}
- </script>
-
- <script>
- angular.module('ngView', [], function($routeProvider, $locationProvider) {
- $routeProvider.when('/Book/:bookId', {
- template: 'examples/book.html',
- controller: BookCntl
- });
- $routeProvider.when('/Book/:bookId/ch/:chapterId', {
- template: 'examples/chapter.html',
- controller: ChapterCntl
- });
-
- // configure html5 to get links working on jsfiddle
- $locationProvider.html5Mode(true);
- });
-
- function MainCntl($scope, $route, $routeParams, $location) {
- $scope.$route = $route;
- $scope.$location = $location;
- $scope.$routeParams = $routeParams;
- }
-
- function BookCntl($scope, $routeParams) {
- $scope.name = "BookCntl";
- $scope.params = $routeParams;
- }
-
- function ChapterCntl($scope, $routeParams) {
- $scope.name = "ChapterCntl";
- $scope.params = $routeParams;
- }
- </script>
-
- <div ng:controller="MainCntl">
- Choose:
- <a href="/Book/Moby">Moby</a> |
- <a href="/Book/Moby/ch/1">Moby: Ch1</a> |
- <a href="/Book/Gatsby">Gatsby</a> |
- <a href="/Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
- <a href="/Book/Scarlet">Scarlet Letter</a><br/>
-
- <ng:view></ng:view>
- <hr />
-
- <pre>$location.path() = {{$location.path()}}</pre>
- <pre>$route.current.template = {{$route.current.template}}</pre>
- <pre>$route.current.params = {{$route.current.params}}</pre>
- <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
- <pre>$routeParams = {{$routeParams}}</pre>
- </div>
- </doc:source>
- <doc:scenario>
- it('should load and compile correct template', function() {
- element('a:contains("Moby: Ch1")').click();
- var content = element('.doc-example-live ng\\:view').text();
- expect(content).toMatch(/controller\: ChapterCntl/);
- expect(content).toMatch(/Book Id\: Moby/);
- expect(content).toMatch(/Chapter Id\: 1/);
-
- element('a:contains("Scarlet")').click();
- content = element('.doc-example-live ng\\:view').text();
- expect(content).toMatch(/controller\: BookCntl/);
- expect(content).toMatch(/Book Id\: Scarlet/);
- });
- </doc:scenario>
- </doc:example>
- */
-var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$compile',
- '$controller',
- function($http, $templateCache, $route, $anchorScroll, $compile,
- $controller) {
- return {
- restrict: 'ECA',
- terminal: true,
- link: function(scope, element) {
- var changeCounter = 0,
- lastScope;
-
- scope.$on('$afterRouteChange', function(event, next, previous) {
- changeCounter++;
- });
-
- scope.$watch(function() {return changeCounter;}, function(newChangeCounter) {
- var template = $route.current && $route.current.template;
-
- function destroyLastScope() {
- if (lastScope) {
- lastScope.$destroy();
- lastScope = null;
- }
- }
-
- function clearContent() {
- // ignore callback if another route change occured since
- if (newChangeCounter == changeCounter) {
- element.html('');
- }
- destroyLastScope();
- }
-
- if (template) {
- $http.get(template, {cache: $templateCache}).success(function(response) {
- // ignore callback if another route change occured since
- if (newChangeCounter == changeCounter) {
- element.html(response);
- destroyLastScope();
-
- var link = $compile(element.contents()),
- current = $route.current;
-
- lastScope = current.scope = scope.$new();
- if (current.controller) {
- $controller(current.controller, {$scope: lastScope});
- }
-
- link(lastScope);
- lastScope.$emit('$contentLoaded');
-
- // $anchorScroll might listen on event...
- $anchorScroll();
- }
- }).error(clearContent);
- } else {
- clearContent();
- }
- });
- }
- };
-}];
-
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.ng:pluralize
- * @restrict EA
- *
- * @description
- * # Overview
- * ng:pluralize is a widget that displays messages according to en-US localization rules.
- * These rules are bundled with angular.js and the rules can be overridden
- * (see {@link guide/dev_guide.i18n Angular i18n} dev guide). You configure ng:pluralize by
- * specifying the mappings between
- * {@link http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
- * plural categories} and the strings to be displayed.
- *
- * # Plural categories and explicit number rules
- * There are two
- * {@link http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
- * plural categories} in Angular's default en-US locale: "one" and "other".
- *
- * While a pural category may match many numbers (for example, in en-US locale, "other" can match
- * any number that is not 1), an explicit number rule can only match one number. For example, the
- * explicit number rule for "3" matches the number 3. You will see the use of plural categories
- * and explicit number rules throughout later parts of this documentation.
- *
- * # Configuring ng:pluralize
- * You configure ng:pluralize by providing 2 attributes: `count` and `when`.
- * You can also provide an optional attribute, `offset`.
- *
- * The value of the `count` attribute can be either a string or an {@link guide/dev_guide.expressions
- * Angular expression}; these are evaluated on the current scope for its binded value.
- *
- * The `when` attribute specifies the mappings between plural categories and the actual
- * string to be displayed. The value of the attribute should be a JSON object so that Angular
- * can interpret it correctly.
- *
- * The following example shows how to configure ng:pluralize:
- *
- * <pre>
- * <ng:pluralize count="personCount"
- when="{'0': 'Nobody is viewing.',
- * 'one': '1 person is viewing.',
- * 'other': '{} people are viewing.'}">
- * </ng:pluralize>
- *</pre>
- *
- * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
- * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
- * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
- * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
- * show "a dozen people are viewing".
- *
- * You can use a set of closed braces(`{}`) as a placeholder for the number that you want substituted
- * into pluralized strings. In the previous example, Angular will replace `{}` with
- * <span ng:non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
- * for <span ng:non-bindable>{{numberExpression}}</span>.
- *
- * # Configuring ng:pluralize with offset
- * The `offset` attribute allows further customization of pluralized text, which can result in
- * a better user experience. For example, instead of the message "4 people are viewing this document",
- * you might display "John, Kate and 2 others are viewing this document".
- * The offset attribute allows you to offset a number by any desired value.
- * Let's take a look at an example:
- *
- * <pre>
- * <ng:pluralize count="personCount" offset=2
- * when="{'0': 'Nobody is viewing.',
- * '1': '{{person1}} is viewing.',
- * '2': '{{person1}} and {{person2}} are viewing.',
- * 'one': '{{person1}}, {{person2}} and one other person are viewing.',
- * 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
- * </ng:pluralize>
- * </pre>
- *
- * Notice that we are still using two plural categories(one, other), but we added
- * three explicit number rules 0, 1 and 2.
- * When one person, perhaps John, views the document, "John is viewing" will be shown.
- * When three people view the document, no explicit number rule is found, so
- * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
- * In this case, plural category 'one' is matched and "John, Marry and one other person are viewing"
- * is shown.
- *
- * Note that when you specify offsets, you must provide explicit number rules for
- * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
- * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
- * plural categories "one" and "other".
- *
- * @param {string|expression} count The variable to be bounded to.
- * @param {string} when The mapping between plural category to its correspoding strings.
- * @param {number=} offset Offset to deduct from the total number.
- *
- * @example
- <doc:example>
- <doc:source>
- <script>
- function Ctrl($scope) {
- $scope.person1 = 'Igor';
- $scope.person2 = 'Misko';
- $scope.personCount = 1;
- }
- </script>
- <div ng:controller="Ctrl">
- Person 1:<input type="text" ng:model="person1" value="Igor" /><br/>
- Person 2:<input type="text" ng:model="person2" value="Misko" /><br/>
- Number of People:<input type="text" ng:model="personCount" value="1" /><br/>
-
- <!--- Example with simple pluralization rules for en locale --->
- Without Offset:
- <ng-pluralize count="personCount"
- when="{'0': 'Nobody is viewing.',
- 'one': '1 person is viewing.',
- 'other': '{} people are viewing.'}">
- </ng-pluralize><br>
-
- <!--- Example with offset --->
- With Offset(2):
- <ng-pluralize count="personCount" offset=2
- when="{'0': 'Nobody is viewing.',
- '1': '{{person1}} is viewing.',
- '2': '{{person1}} and {{person2}} are viewing.',
- 'one': '{{person1}}, {{person2}} and one other person are viewing.',
- 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
- </ng-pluralize>
- </div>
- </doc:source>
- <doc:scenario>
- it('should show correct pluralized string', function() {
- expect(element('.doc-example-live ng-pluralize:first').text()).
- toBe('1 person is viewing.');
- expect(element('.doc-example-live ng-pluralize:last').text()).
- toBe('Igor is viewing.');
-
- using('.doc-example-live').input('personCount').enter('0');
- expect(element('.doc-example-live ng-pluralize:first').text()).
- toBe('Nobody is viewing.');
- expect(element('.doc-example-live ng-pluralize:last').text()).
- toBe('Nobody is viewing.');
-
- using('.doc-example-live').input('personCount').enter('2');
- expect(element('.doc-example-live ng-pluralize:first').text()).
- toBe('2 people are viewing.');
- expect(element('.doc-example-live ng-pluralize:last').text()).
- toBe('Igor and Misko are viewing.');
-
- using('.doc-example-live').input('personCount').enter('3');
- expect(element('.doc-example-live ng-pluralize:first').text()).
- toBe('3 people are viewing.');
- expect(element('.doc-example-live ng-pluralize:last').text()).
- toBe('Igor, Misko and one other person are viewing.');
-
- using('.doc-example-live').input('personCount').enter('4');
- expect(element('.doc-example-live ng-pluralize:first').text()).
- toBe('4 people are viewing.');
- expect(element('.doc-example-live ng-pluralize:last').text()).
- toBe('Igor, Misko and 2 other people are viewing.');
- });
-
- it('should show data-binded names', function() {
- using('.doc-example-live').input('personCount').enter('4');
- expect(element('.doc-example-live ng-pluralize:last').text()).
- toBe('Igor, Misko and 2 other people are viewing.');
-
- using('.doc-example-live').input('person1').enter('Di');
- using('.doc-example-live').input('person2').enter('Vojta');
- expect(element('.doc-example-live ng-pluralize:last').text()).
- toBe('Di, Vojta and 2 other people are viewing.');
- });
- </doc:scenario>
- </doc:example>
- */
-var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) {
- var BRACE = /{}/g;
- return {
- restrict: 'EA',
- link: function(scope, element, attr) {
- var numberExp = attr.count,
- whenExp = element.attr(attr.$attr.when), // this is because we have {{}} in attrs
- offset = attr.offset || 0,
- whens = scope.$eval(whenExp),
- whensExpFns = {};
-
- forEach(whens, function(expression, key) {
- whensExpFns[key] =
- $interpolate(expression.replace(BRACE, '{{' + numberExp + '-' + offset + '}}'));
- });
-
- scope.$watch(function() {
- var value = parseFloat(scope.$eval(numberExp));
-
- if (!isNaN(value)) {
- //if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise,
- //check it against pluralization rules in $locale service
- if (!whens[value]) value = $locale.pluralCat(value - offset);
- return whensExpFns[value](scope, element, true);
- } else {
- return '';
- }
- }, function(newVal) {
- element.text(newVal);
- });
- }
- };
-}];
-
-
-/**
- * @ngdoc directive
- * @name angular.module.ng.$compileProvider.directive.script
- *
- * @description
- * Load content of a script tag, with type `text/ng-template`, into `$templateCache`, so that the
- * template can be used by `ng:include`, `ng:view` or directive templates.
- *
- * @restrict E
- *
- * @example
- <doc:example>
- <doc:source>
- <script type="text/ng-template" id="/tpl.html">
- Content of the template.
- </script>
-
- <a ng:click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
- <div id="tpl-content" ng-include src="currentTpl"></div>
- </doc:source>
- <doc:scenario>
- it('should load template defined inside script tag', function() {
- element('#tpl-link').click();
- expect(element('#tpl-content').text()).toMatch(/Content of the template/);
- });
- </doc:scenario>
- </doc:example>
- */
-var scriptDirective = ['$templateCache', function($templateCache) {
- return {
- restrict: 'E',
- terminal: true,
- compile: function(element, attr) {
- if (attr.type == 'text/ng-template') {
- var templateUrl = attr.id;
- $templateCache.put(templateUrl, element.text());
- }
- }
- };
-}];