diff options
| author | Igor Minar | 2012-03-08 15:00:38 -0800 | 
|---|---|---|
| committer | Igor Minar | 2012-03-08 22:29:34 -0800 | 
| commit | f54db2ccda399f2677e4ca7588018cb31545a2b4 (patch) | |
| tree | 29ef2f8f834544c84cea1a82e3d08679358fb992 /src | |
| parent | dd7b0f56fcd9785f7fccae8c4f088a8f3e7b125e (diff) | |
| download | angular.js-f54db2ccda399f2677e4ca7588018cb31545a2b4.tar.bz2 | |
chore(directives,widgets): reorg the code under directive/ dir
Diffstat (limited to 'src')
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     </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}}       +           </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}}       +           </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     </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}}       -           </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}}       -           </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()); -      } -    } -  }; -}]; | 
