diff options
| author | Misko Hevery | 2012-03-23 14:03:24 -0700 |
|---|---|---|
| committer | Misko Hevery | 2012-03-28 11:16:35 -0700 |
| commit | 2430f52bb97fa9d682e5f028c977c5bf94c5ec38 (patch) | |
| tree | e7529b741d70199f36d52090b430510bad07f233 /src/directive | |
| parent | 944098a4e0f753f06b40c73ca3e79991cec6c2e2 (diff) | |
| download | angular.js-2430f52bb97fa9d682e5f028c977c5bf94c5ec38.tar.bz2 | |
chore(module): move files around in preparation for more modules
Diffstat (limited to 'src/directive')
| -rw-r--r-- | src/directive/a.js | 29 | ||||
| -rw-r--r-- | src/directive/booleanAttrDirs.js | 314 | ||||
| -rw-r--r-- | src/directive/directives.js | 11 | ||||
| -rw-r--r-- | src/directive/form.js | 267 | ||||
| -rw-r--r-- | src/directive/input.js | 1194 | ||||
| -rw-r--r-- | src/directive/ngBind.js | 155 | ||||
| -rw-r--r-- | src/directive/ngClass.js | 143 | ||||
| -rw-r--r-- | src/directive/ngCloak.js | 61 | ||||
| -rw-r--r-- | src/directive/ngController.js | 103 | ||||
| -rw-r--r-- | src/directive/ngEventDirs.js | 222 | ||||
| -rw-r--r-- | src/directive/ngInclude.js | 131 | ||||
| -rw-r--r-- | src/directive/ngInit.js | 37 | ||||
| -rw-r--r-- | src/directive/ngNonBindable.js | 33 | ||||
| -rw-r--r-- | src/directive/ngPluralize.js | 204 | ||||
| -rw-r--r-- | src/directive/ngRepeat.js | 181 | ||||
| -rw-r--r-- | src/directive/ngShowHide.js | 80 | ||||
| -rw-r--r-- | src/directive/ngStyle.js | 42 | ||||
| -rw-r--r-- | src/directive/ngSwitch.js | 112 | ||||
| -rw-r--r-- | src/directive/ngTransclude.js | 58 | ||||
| -rw-r--r-- | src/directive/ngView.js | 170 | ||||
| -rw-r--r-- | src/directive/script.js | 43 | ||||
| -rw-r--r-- | src/directive/select.js | 449 | ||||
| -rw-r--r-- | src/directive/style.js | 6 |
23 files changed, 0 insertions, 4045 deletions
diff --git a/src/directive/a.js b/src/directive/a.js deleted file mode 100644 index d96af784..00000000 --- a/src/directive/a.js +++ /dev/null @@ -1,29 +0,0 @@ -'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/directive/booleanAttrDirs.js b/src/directive/booleanAttrDirs.js deleted file mode 100644 index 7da52db0..00000000 --- a/src/directive/booleanAttrDirs.js +++ /dev/null @@ -1,314 +0,0 @@ -'use strict'; - -/** - * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-href - * @restrict A - * - * @description - * Using <angular/> markup like {{hash}} in an href attribute makes - * the page open to a wrong URL, if the user clicks that link before - * angular has a chance to replace the {{hash}} with actual URL, the - * link will be broken and will most likely return a 404 error. - * The `ng-href` solves this problem by placing the `href` in the - * `ng-` namespace. - * - * The buggy way to write it: - * <pre> - * <a href="http://www.gravatar.com/avatar/{{hash}}"/> - * </pre> - * - * The correct way to write it: - * <pre> - * <a ng-href="http://www.gravatar.com/avatar/{{hash}}"/> - * </pre> - * - * @element A - * @param {template} ng-href any string which can contain `{{}}` markup. - * - * @example - * This example uses `link` variable inside `href` attribute: - <doc:example> - <doc:source> - <input ng-model="value" /><br /> - <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br /> - <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br /> - <a id="link-3" ng-href="/{{'123'}}" ng-ext-link>link 3</a> (link, reload!)<br /> - <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br /> - <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br /> - <a id="link-6" ng-href="/{{value}}" ng-ext-link>link</a> (link, change hash) - </doc:source> - <doc:scenario> - it('should execute ng-click but not reload when href without value', function() { - element('#link-1').click(); - expect(input('value').val()).toEqual('1'); - expect(element('#link-1').attr('href')).toBe(""); - }); - - it('should execute ng-click but not reload when href empty string', function() { - element('#link-2').click(); - expect(input('value').val()).toEqual('2'); - expect(element('#link-2').attr('href')).toBe(""); - }); - - it('should execute ng-click and change url when ng-href specified', function() { - expect(element('#link-3').attr('href')).toBe("/123"); - - element('#link-3').click(); - expect(browser().window().path()).toEqual('/123'); - }); - - it('should execute ng-click but not reload when href empty string and name specified', function() { - element('#link-4').click(); - expect(input('value').val()).toEqual('4'); - expect(element('#link-4').attr('href')).toBe(""); - }); - - it('should execute ng-click but not reload when no href but name specified', function() { - element('#link-5').click(); - expect(input('value').val()).toEqual('5'); - expect(element('#link-5').attr('href')).toBe(""); - }); - - it('should only change url when only ng-href', function() { - input('value').enter('6'); - expect(element('#link-6').attr('href')).toBe("/6"); - - element('#link-6').click(); - expect(browser().window().path()).toEqual('/6'); - }); - </doc:scenario> - </doc:example> - */ - -/** - * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-src - * @restrict A - * - * @description - * Using <angular/> markup like `{{hash}}` in a `src` attribute doesn't - * work right: The browser will fetch from the URL with the literal - * text `{{hash}}` until <angular/> replaces the expression inside - * `{{hash}}`. The `ng-src` attribute solves this problem by placing - * the `src` attribute in the `ng-` namespace. - * - * The buggy way to write it: - * <pre> - * <img src="http://www.gravatar.com/avatar/{{hash}}"/> - * </pre> - * - * The correct way to write it: - * <pre> - * <img ng-src="http://www.gravatar.com/avatar/{{hash}}"/> - * </pre> - * - * @element IMG - * @param {template} ng-src any string which can contain `{{}}` markup. - */ - -/** - * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-disabled - * @restrict A - * - * @description - * - * The following markup will make the button enabled on Chrome/Firefox but not on IE8 and older IEs: - * <pre> - * <div ng-init="scope = { isDisabled: false }"> - * <button disabled="{{scope.isDisabled}}">Disabled</button> - * </div> - * </pre> - * - * The HTML specs do not require browsers to preserve the special attributes such as disabled. - * (The presence of them means true and absence means false) - * This prevents the angular compiler from correctly retrieving the binding expression. - * To solve this problem, we introduce ng-disabled. - * - * @example - <doc:example> - <doc:source> - Click me to toggle: <input type="checkbox" ng-model="checked"><br/> - <button ng-model="button" ng-disabled="checked">Button</button> - </doc:source> - <doc:scenario> - it('should toggle button', function() { - expect(element('.doc-example-live :button').prop('disabled')).toBeFalsy(); - input('checked').check(); - expect(element('.doc-example-live :button').prop('disabled')).toBeTruthy(); - }); - </doc:scenario> - </doc:example> - * - * @element INPUT - * @param {string} expression Angular expression that will be evaluated. - */ - - -/** - * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-checked - * @restrict A - * - * @description - * The HTML specs do not require browsers to preserve the special attributes such as checked. - * (The presence of them means true and absence means false) - * This prevents the angular compiler from correctly retrieving the binding expression. - * To solve this problem, we introduce ng-checked. - * @example - <doc:example> - <doc:source> - Check me to check both: <input type="checkbox" ng-model="master"><br/> - <input id="checkSlave" type="checkbox" ng-checked="master"> - </doc:source> - <doc:scenario> - it('should check both checkBoxes', function() { - expect(element('.doc-example-live #checkSlave').prop('checked')).toBeFalsy(); - input('master').check(); - expect(element('.doc-example-live #checkSlave').prop('checked')).toBeTruthy(); - }); - </doc:scenario> - </doc:example> - * - * @element INPUT - * @param {string} expression Angular expression that will be evaluated. - */ - - -/** - * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-multiple - * @restrict A - * - * @description - * The HTML specs do not require browsers to preserve the special attributes such as multiple. - * (The presence of them means true and absence means false) - * This prevents the angular compiler from correctly retrieving the binding expression. - * To solve this problem, we introduce ng-multiple. - * - * @example - <doc:example> - <doc:source> - Check me check multiple: <input type="checkbox" ng-model="checked"><br/> - <select id="select" ng-multiple="checked"> - <option>Misko</option> - <option>Igor</option> - <option>Vojta</option> - <option>Di</option> - </select> - </doc:source> - <doc:scenario> - it('should toggle multiple', function() { - expect(element('.doc-example-live #select').prop('multiple')).toBeFalsy(); - input('checked').check(); - expect(element('.doc-example-live #select').prop('multiple')).toBeTruthy(); - }); - </doc:scenario> - </doc:example> - * - * @element SELECT - * @param {string} expression Angular expression that will be evaluated. - */ - - -/** - * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-readonly - * @restrict A - * - * @description - * The HTML specs do not require browsers to preserve the special attributes such as readonly. - * (The presence of them means true and absence means false) - * This prevents the angular compiler from correctly retrieving the binding expression. - * To solve this problem, we introduce ng-readonly. - * @example - <doc:example> - <doc:source> - Check me to make text readonly: <input type="checkbox" ng-model="checked"><br/> - <input type="text" ng-readonly="checked" value="I'm Angular"/> - </doc:source> - <doc:scenario> - it('should toggle readonly attr', function() { - expect(element('.doc-example-live :text').prop('readonly')).toBeFalsy(); - input('checked').check(); - expect(element('.doc-example-live :text').prop('readonly')).toBeTruthy(); - }); - </doc:scenario> - </doc:example> - * - * @element INPUT - * @param {string} expression Angular expression that will be evaluated. - */ - - -/** - * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-selected - * @restrict A - * - * @description - * The HTML specs do not require browsers to preserve the special attributes such as selected. - * (The presence of them means true and absence means false) - * This prevents the angular compiler from correctly retrieving the binding expression. - * To solve this problem, we introduce ng-selected. - * @example - <doc:example> - <doc:source> - Check me to select: <input type="checkbox" ng-model="selected"><br/> - <select> - <option>Hello!</option> - <option id="greet" ng-selected="selected">Greetings!</option> - </select> - </doc:source> - <doc:scenario> - it('should select Greetings!', function() { - expect(element('.doc-example-live #greet').prop('selected')).toBeFalsy(); - input('selected').check(); - expect(element('.doc-example-live #greet').prop('selected')).toBeTruthy(); - }); - </doc:scenario> - </doc:example> - * - * @element OPTION - * @param {string} expression Angular expression that will be evaluated. - */ - - -var ngAttributeAliasDirectives = {}; - - -// boolean attrs are evaluated -forEach(BOOLEAN_ATTR, function(propName, attrName) { - var normalized = directiveNormalize('ng-' + attrName); - ngAttributeAliasDirectives[normalized] = function() { - return { - compile: function(tpl, attr) { - attr.$observers[attrName] = []; - return function(scope, element, attr) { - scope.$watch(attr[normalized], function(value) { - attr.$set(attrName, value); - }); - }; - } - }; - }; -}); - - -// ng-src, ng-href are interpolated -forEach(['src', 'href'], function(attrName) { - var normalized = directiveNormalize('ng-' + attrName); - ngAttributeAliasDirectives[normalized] = function() { - return { - compile: function(tpl, attr) { - attr.$observers[attrName] = []; - return function(scope, element, attr) { - attr.$observe(normalized, function(value) { - attr.$set(attrName, value); - }); - }; - } - }; - }; -}); diff --git a/src/directive/directives.js b/src/directive/directives.js deleted file mode 100644 index 123645f9..00000000 --- a/src/directive/directives.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict'; - -function ngDirective(directive) { - if (isFunction(directive)) { - directive = { - link: directive - } - } - directive.restrict = directive.restrict || 'AC'; - return valueFn(directive); -}; diff --git a/src/directive/form.js b/src/directive/form.js deleted file mode 100644 index b6d3f4be..00000000 --- a/src/directive/form.js +++ /dev/null @@ -1,267 +0,0 @@ -'use strict'; - - -var nullFormCtrl = { - $addControl: noop, - $removeControl: noop, - $setValidity: noop, - $setDirty: noop -} - -/** - * @ngdoc object - * @name angular.module.ng.$compileProvider.directive.form.FormController - * - * @property {boolean} $pristine True if user has not interacted with the form yet. - * @property {boolean} $dirty True if user has already interacted with the form. - * @property {boolean} $valid True if all of the containg forms and controls are valid. - * @property {boolean} $invalid True if at least one containing control or form is invalid. - * - * @property {Object} $error Is an object hash, containing references to all invalid controls or - * forms, where: - * - * - keys are validation tokens (error names) — such as `REQUIRED`, `URL` or `EMAIL`), - * - values are arrays of controls or forms that are invalid with given error. - * - * @description - * `FormController` keeps track of all its controls and nested forms as well as state of them, - * such as being valid/invalid or dirty/pristine. - * - * Each {@link angular.module.ng.$compileProvider.directive.form form} directive creates an instance - * of `FormController`. - * - */ -FormController.$inject = ['$element', '$attrs']; -function FormController(element, attrs) { - var form = this, - parentForm = element.parent().controller('form') || nullFormCtrl, - invalidCount = 0, // used to easily determine if we are valid - errors = form.$error = {}; - - // init state - form.$name = attrs.name; - form.$dirty = false; - form.$pristine = true; - form.$valid = true; - form.$invalid = false; - - parentForm.$addControl(form); - - // Setup initial state of the control - element.addClass(PRISTINE_CLASS); - toggleValidCss(true); - - // convenience method for easy toggling of classes - function toggleValidCss(isValid, validationErrorKey) { - validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : ''; - element. - removeClass((isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey). - addClass((isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey); - } - - form.$addControl = function(control) { - if (control.$name && !form.hasOwnProperty(control.$name)) { - form[control.$name] = control; - } - }; - - form.$removeControl = function(control) { - if (control.$name && form[control.$name] === control) { - delete form[control.$name]; - } - forEach(errors, cleanupControlErrors, control); - }; - - form.$setValidity = function(validationToken, isValid, control) { - if (isValid) { - cleanupControlErrors(errors[validationToken], validationToken, control); - - if (!invalidCount) { - toggleValidCss(isValid); - form.$valid = true; - form.$invalid = false; - } - } else { - if (!invalidCount) { - toggleValidCss(isValid); - } - addControlError(validationToken, control); - - form.$valid = false; - form.$invalid = true; - } - }; - - form.$setDirty = function() { - element.removeClass(PRISTINE_CLASS).addClass(DIRTY_CLASS); - form.$dirty = true; - form.$pristine = false; - }; - - function cleanupControlErrors(queue, validationToken, control) { - if (queue) { - control = control || this; // so that we can be used in forEach; - arrayRemove(queue, control); - if (!queue.length) { - invalidCount--; - errors[validationToken] = false; - toggleValidCss(true, validationToken); - parentForm.$setValidity(validationToken, true, form); - } - } - } - - function addControlError(validationToken, control) { - var queue = errors[validationToken]; - if (queue) { - if (includes(queue, control)) return; - } else { - errors[validationToken] = queue = []; - invalidCount++; - toggleValidCss(false, validationToken); - parentForm.$setValidity(validationToken, false, form); - } - queue.push(control); - } -} - - -/** - * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-form - * @restrict EAC - * - * @description - * Nestable alias of {@link angular.module.ng.$compileProvider.directive.form `form`} directive. HTML - * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a - * sub-group of controls needs to be determined. - * - * @param {string=} ng-form|name Name of the form. If specified, the form controller will be published into - * related scope, under this name. - * - */ - - /** - * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.form - * @restrict E - * - * @description - * Directive that instantiates - * {@link angular.module.ng.$compileProvider.directive.form.FormController FormController}. - * - * If `name` attribute is specified, the form controller is published onto the current scope under - * this name. - * - * # Alias: {@link angular.module.ng.$compileProvider.directive.ng-form `ng-form`} - * - * In angular forms can be nested. This means that the outer form is valid when all of the child - * forms are valid as well. However browsers do not allow nesting of `<form>` elements, for this - * reason angular provides {@link angular.module.ng.$compileProvider.directive.ng-form `ng-form`} alias - * which behaves identical to `<form>` but allows form nesting. - * - * - * # CSS classes - * - `ng-valid` Is set if the form is valid. - * - `ng-invalid` Is set if the form is invalid. - * - `ng-pristine` Is set if the form is pristine. - * - `ng-dirty` Is set if the form is dirty. - * - * - * # Submitting a form and preventing default action - * - * Since the role of forms in client-side Angular applications is different than in classical - * roundtrip apps, it is desirable for the browser not to translate the form submission into a full - * page reload that sends the data to the server. Instead some javascript logic should be triggered - * to handle the form submission in application specific way. - * - * For this reason, Angular prevents the default action (form submission to the server) unless the - * `<form>` element has an `action` attribute specified. - * - * You can use one of the following two ways to specify what javascript method should be called when - * a form is submitted: - * - * - ng-submit on the form element (add link to ng-submit) - * - ng-click on the first button or input field of type submit (input[type=submit]) - * - * To prevent double execution of the handler, use only one of ng-submit or ng-click. This is - * because of the following form submission rules coming from the html spec: - * - * - If a form has only one input field then hitting enter in this field triggers form submit - * (`ng-submit`) - * - if a form has has 2+ input fields and no buttons or input[type=submit] then hitting enter - * doesn't trigger submit - * - if a form has one or more input fields and one or more buttons or input[type=submit] then - * hitting enter in any of the input fields will trigger the click handler on the *first* button or - * input[type=submit] (`ng-click`) *and* a submit handler on the enclosing form (`ng-submit`) - * - * @param {string=} name Name of the form. If specified, the form controller will be published into - * related scope, under this name. - * - * @example - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.userType = 'guest'; - } - </script> - <form name="myForm" ng-controller="Ctrl"> - userType: <input name="input" ng-model="userType" required> - <span class="error" ng-show="myForm.input.$error.REQUIRED">Required!</span><br> - <tt>userType = {{userType}}</tt><br> - <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br> - <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br> - <tt>myForm.$valid = {{myForm.$valid}}</tt><br> - <tt>myForm.$error.REQUIRED = {{!!myForm.$error.REQUIRED}}</tt><br> - </form> - </doc:source> - <doc:scenario> - it('should initialize to model', function() { - expect(binding('userType')).toEqual('guest'); - expect(binding('myForm.input.$valid')).toEqual('true'); - }); - - it('should be invalid if empty', function() { - input('userType').enter(''); - expect(binding('userType')).toEqual(''); - expect(binding('myForm.input.$valid')).toEqual('false'); - }); - </doc:scenario> - </doc:example> - */ -var formDirectiveDir = { - name: 'form', - restrict: 'E', - controller: FormController, - compile: function() { - return { - pre: function(scope, formElement, attr, controller) { - if (!attr.action) { - formElement.bind('submit', function(event) { - event.preventDefault(); - }); - } - - var parentFormCtrl = formElement.parent().controller('form'), - alias = attr.name || attr.ngForm; - - if (alias) { - scope[alias] = controller; - } - if (parentFormCtrl) { - formElement.bind('$destroy', function() { - parentFormCtrl.$removeControl(controller); - if (alias) { - scope[alias] = undefined; - } - extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards - }); - } - } - }; - } -}; - -var formDirective = valueFn(formDirectiveDir); -var ngFormDirective = valueFn(extend(copy(formDirectiveDir), {restrict: 'EAC'})); diff --git a/src/directive/input.js b/src/directive/input.js deleted file mode 100644 index 348c9f25..00000000 --- a/src/directive/input.js +++ /dev/null @@ -1,1194 +0,0 @@ -'use strict'; - -var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/; -var EMAIL_REGEXP = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/; -var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/; - -var inputType = { - - /** - * @ngdoc inputType - * @name angular.module.ng.$compileProvider.directive.input.text - * - * @description - * Standard HTML text input with angular data binding. - * - * @param {string} ng-model Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {number=} ng-minlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ng-maxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. - * @param {string=} ng-pattern Sets `pattern` validation error key if the value does not match the - * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for - * patterns defined as scope expressions. - * @param {string=} ng-change Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.text = 'guest'; - $scope.word = /^\w*$/; - } - </script> - <form name="myForm" ng-controller="Ctrl"> - Single word: <input type="text" name="input" ng-model="text" - ng-pattern="word" required> - <span class="error" ng-show="myForm.input.$error.required"> - Required!</span> - <span class="error" ng-show="myForm.input.$error.pattern"> - Single word only!</span> - - <tt>text = {{text}}</tt><br/> - <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> - <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> - <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> - <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> - </form> - </doc:source> - <doc:scenario> - it('should initialize to model', function() { - expect(binding('text')).toEqual('guest'); - expect(binding('myForm.input.$valid')).toEqual('true'); - }); - - it('should be invalid if empty', function() { - input('text').enter(''); - expect(binding('text')).toEqual(''); - expect(binding('myForm.input.$valid')).toEqual('false'); - }); - - it('should be invalid if multi word', function() { - input('text').enter('hello world'); - expect(binding('myForm.input.$valid')).toEqual('false'); - }); - </doc:scenario> - </doc:example> - */ - 'text': textInputType, - - - /** - * @ngdoc inputType - * @name angular.module.ng.$compileProvider.directive.input.number - * - * @description - * Text input with number validation and transformation. Sets the `number` validation - * error if not a valid number. - * - * @param {string} ng-model Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} min Sets the `min` validation error key if the value entered is less then `min`. - * @param {string=} max Sets the `max` validation error key if the value entered is greater then `min`. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {number=} ng-minlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ng-maxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. - * @param {string=} ng-pattern Sets `pattern` validation error key if the value does not match the - * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for - * patterns defined as scope expressions. - * @param {string=} ng-change Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.value = 12; - } - </script> - <form name="myForm" ng-controller="Ctrl"> - Number: <input type="number" name="input" ng-model="value" - min="0" max="99" required> - <span class="error" ng-show="myForm.list.$error.required"> - Required!</span> - <span class="error" ng-show="myForm.list.$error.number"> - Not valid number!</span> - <tt>value = {{value}}</tt><br/> - <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> - <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> - <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> - <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> - </form> - </doc:source> - <doc:scenario> - it('should initialize to model', function() { - expect(binding('value')).toEqual('12'); - expect(binding('myForm.input.$valid')).toEqual('true'); - }); - - it('should be invalid if empty', function() { - input('value').enter(''); - expect(binding('value')).toEqual(''); - expect(binding('myForm.input.$valid')).toEqual('false'); - }); - - it('should be invalid if over max', function() { - input('value').enter('123'); - expect(binding('value')).toEqual(''); - expect(binding('myForm.input.$valid')).toEqual('false'); - }); - </doc:scenario> - </doc:example> - */ - 'number': numberInputType, - - - /** - * @ngdoc inputType - * @name angular.module.ng.$compileProvider.directive.input.url - * - * @description - * Text input with URL validation. Sets the `url` validation error key if the content is not a - * valid URL. - * - * @param {string} ng-model Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {number=} ng-minlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ng-maxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. - * @param {string=} ng-pattern Sets `pattern` validation error key if the value does not match the - * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for - * patterns defined as scope expressions. - * @param {string=} ng-change Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.text = 'http://google.com'; - } - </script> - <form name="myForm" ng-controller="Ctrl"> - URL: <input type="url" name="input" ng-model="text" required> - <span class="error" ng-show="myForm.input.$error.required"> - Required!</span> - <span class="error" ng-show="myForm.input.$error.url"> - Not valid url!</span> - <tt>text = {{text}}</tt><br/> - <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> - <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> - <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> - <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> - <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/> - </form> - </doc:source> - <doc:scenario> - it('should initialize to model', function() { - expect(binding('text')).toEqual('http://google.com'); - expect(binding('myForm.input.$valid')).toEqual('true'); - }); - - it('should be invalid if empty', function() { - input('text').enter(''); - expect(binding('text')).toEqual(''); - expect(binding('myForm.input.$valid')).toEqual('false'); - }); - - it('should be invalid if not url', function() { - input('text').enter('xxx'); - expect(binding('myForm.input.$valid')).toEqual('false'); - }); - </doc:scenario> - </doc:example> - */ - 'url': urlInputType, - - - /** - * @ngdoc inputType - * @name angular.module.ng.$compileProvider.directive.input.email - * - * @description - * Text input with email validation. Sets the `email` validation error key if not a valid email - * address. - * - * @param {string} ng-model Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {number=} ng-minlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ng-maxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. - * @param {string=} ng-pattern Sets `pattern` validation error key if the value does not match the - * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for - * patterns defined as scope expressions. - * - * @example - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.text = 'me@example.com'; - } - </script> - <form name="myForm" ng-controller="Ctrl"> - Email: <input type="email" name="input" ng-model="text" required> - <span class="error" ng-show="myForm.input.$error.required"> - Required!</span> - <span class="error" ng-show="myForm.input.$error.email"> - Not valid email!</span> - <tt>text = {{text}}</tt><br/> - <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> - <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> - <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> - <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> - <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/> - </form> - </doc:source> - <doc:scenario> - it('should initialize to model', function() { - expect(binding('text')).toEqual('me@example.com'); - expect(binding('myForm.input.$valid')).toEqual('true'); - }); - - it('should be invalid if empty', function() { - input('text').enter(''); - expect(binding('text')).toEqual(''); - expect(binding('myForm.input.$valid')).toEqual('false'); - }); - - it('should be invalid if not email', function() { - input('text').enter('xxx'); - expect(binding('myForm.input.$valid')).toEqual('false'); - }); - </doc:scenario> - </doc:example> - */ - 'email': emailInputType, - - - /** - * @ngdoc inputType - * @name angular.module.ng.$compileProvider.directive.input.radio - * - * @description - * HTML radio button. - * - * @param {string} ng-model Assignable angular expression to data-bind to. - * @param {string} value The value to which the expression should be set when selected. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} ng-change Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.color = 'blue'; - } - </script> - <form name="myForm" ng-controller="Ctrl"> - <input type="radio" ng-model="color" value="red"> Red <br/> - <input type="radio" ng-model="color" value="green"> Green <br/> - <input type="radio" ng-model="color" value="blue"> Blue <br/> - <tt>color = {{color}}</tt><br/> - </form> - </doc:source> - <doc:scenario> - it('should change state', function() { - expect(binding('color')).toEqual('blue'); - - input('color').select('red'); - expect(binding('color')).toEqual('red'); - }); - </doc:scenario> - </doc:example> - */ - 'radio': radioInputType, - - - /** - * @ngdoc inputType - * @name angular.module.ng.$compileProvider.directive.input.checkbox - * - * @description - * HTML checkbox. - * - * @param {string} ng-model Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} ng-true-value The value to which the expression should be set when selected. - * @param {string=} ng-false-value The value to which the expression should be set when not selected. - * @param {string=} ng-change Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.value1 = true; - $scope.value2 = 'YES' - } - </script> - <form name="myForm" ng-controller="Ctrl"> - Value1: <input type="checkbox" ng-model="value1"> <br/> - Value2: <input type="checkbox" ng-model="value2" - ng-true-value="YES" ng-false-value="NO"> <br/> - <tt>value1 = {{value1}}</tt><br/> - <tt>value2 = {{value2}}</tt><br/> - </form> - </doc:source> - <doc:scenario> - it('should change state', function() { - expect(binding('value1')).toEqual('true'); - expect(binding('value2')).toEqual('YES'); - - input('value1').check(); - input('value2').check(); - expect(binding('value1')).toEqual('false'); - expect(binding('value2')).toEqual('NO'); - }); - </doc:scenario> - </doc:example> - */ - 'checkbox': checkboxInputType, - - 'hidden': noop, - 'button': noop, - 'submit': noop, - 'reset': noop -}; - - -function isEmpty(value) { - return isUndefined(value) || value === '' || value === null || value !== value; -} - - -function textInputType(scope, element, attr, ctrl) { - element.bind('blur', function() { - scope.$apply(function() { - ctrl.$setViewValue(trim(element.val())); - }); - }); - - ctrl.$render = function() { - element.val(isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue); - }; - - // pattern validator - var pattern = attr.ngPattern, - patternValidator; - - var validate = function(regexp, value) { - if (isEmpty(value) || regexp.test(value)) { - ctrl.$setValidity('pattern', true); - return value; - } else { - ctrl.$setValidity('pattern', false); - return undefined; - } - }; - - if (pattern) { - if (pattern.match(/^\/(.*)\/$/)) { - pattern = new RegExp(pattern.substr(1, pattern.length - 2)); - patternValidator = function(value) { - return validate(pattern, value) - }; - } else { - patternValidator = function(value) { - var patternObj = scope.$eval(pattern); - - if (!patternObj || !patternObj.test) { - throw new Error('Expected ' + pattern + ' to be a RegExp but was ' + patternObj); - } - return validate(patternObj, value); - }; - } - - ctrl.$formatters.push(patternValidator); - ctrl.$parsers.push(patternValidator); - } - - // min length validator - if (attr.ngMinlength) { - var minlength = int(attr.ngMinlength); - var minLengthValidator = function(value) { - if (!isEmpty(value) && value.length < minlength) { - ctrl.$setValidity('minlength', false); - return undefined; - } else { - ctrl.$setValidity('minlength', true); - return value; - } - }; - - ctrl.$parsers.push(minLengthValidator); - ctrl.$formatters.push(minLengthValidator); - } - - // max length validator - if (attr.ngMaxlength) { - var maxlength = int(attr.ngMaxlength); - var maxLengthValidator = function(value) { - if (!isEmpty(value) && value.length > maxlength) { - ctrl.$setValidity('maxlength', false); - return undefined; - } else { - ctrl.$setValidity('maxlength', true); - return value; - } - }; - - ctrl.$parsers.push(maxLengthValidator); - ctrl.$formatters.push(maxLengthValidator); - } -}; - -function numberInputType(scope, element, attr, ctrl) { - textInputType(scope, element, attr, ctrl); - - ctrl.$parsers.push(function(value) { - var empty = isEmpty(value); - if (empty || NUMBER_REGEXP.test(value)) { - ctrl.$setValidity('number', true); - return value === '' ? null : (empty ? value : parseFloat(value)); - } else { - ctrl.$setValidity('number', false); - return undefined; - } - }); - - ctrl.$formatters.push(function(value) { - return isEmpty(value) ? '' : '' + value; - }); - - if (attr.min) { - var min = parseFloat(attr.min); - var minValidator = function(value) { - if (!isEmpty(value) && value < min) { - ctrl.$setValidity('min', false); - return undefined; - } else { - ctrl.$setValidity('min', true); - return value; - } - }; - - ctrl.$parsers.push(minValidator); - ctrl.$formatters.push(minValidator); - } - - if (attr.max) { - var max = parseFloat(attr.max); - var maxValidator = function(value) { - if (!isEmpty(value) && value > max) { - ctrl.$setValidity('max', false); - return undefined; - } else { - ctrl.$setValidity('max', true); - return value; - } - }; - - ctrl.$parsers.push(maxValidator); - ctrl.$formatters.push(maxValidator); - } - - ctrl.$formatters.push(function(value) { - - if (isEmpty(value) || isNumber(value)) { - ctrl.$setValidity('number', true); - return value; - } else { - ctrl.$setValidity('number', false); - return undefined; - } - }); -} - -function urlInputType(scope, element, attr, ctrl) { - textInputType(scope, element, attr, ctrl); - - var urlValidator = function(value) { - if (isEmpty(value) || URL_REGEXP.test(value)) { - ctrl.$setValidity('url', true); - return value; - } else { - ctrl.$setValidity('url', false); - return undefined; - } - }; - - ctrl.$formatters.push(urlValidator); - ctrl.$parsers.push(urlValidator); -} - -function emailInputType(scope, element, attr, ctrl) { - textInputType(scope, element, attr, ctrl); - - var emailValidator = function(value) { - if (isEmpty(value) || EMAIL_REGEXP.test(value)) { - ctrl.$setValidity('email', true); - return value; - } else { - ctrl.$setValidity('email', false); - return undefined; - } - }; - - ctrl.$formatters.push(emailValidator); - ctrl.$parsers.push(emailValidator); -} - -function radioInputType(scope, element, attr, ctrl) { - // correct the name - element.attr('name', attr.id + '@' + attr.name); - - element.bind('click', function() { - if (element[0].checked) { - scope.$apply(function() { - ctrl.$setViewValue(attr.value); - }); - }; - }); - - ctrl.$render = function() { - var value = attr.value; - element[0].checked = (value == ctrl.$viewValue); - }; - - attr.$observe('value', ctrl.$render); -} - -function checkboxInputType(scope, element, attr, ctrl) { - var trueValue = attr.ngTrueValue, - falseValue = attr.ngFalseValue; - - if (!isString(trueValue)) trueValue = true; - if (!isString(falseValue)) falseValue = false; - - element.bind('click', function() { - scope.$apply(function() { - ctrl.$setViewValue(element[0].checked); - }); - }); - - ctrl.$render = function() { - element[0].checked = ctrl.$viewValue; - }; - - ctrl.$formatters.push(function(value) { - return value === trueValue; - }); - - ctrl.$parsers.push(function(value) { - return value ? trueValue : falseValue; - }); -} - - -/** - * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.textarea - * - * @description - * HTML textarea element control with angular data-binding. The data-binding and validation - * properties of this element are exactly the same as those of the - * {@link angular.module.ng.$compileProvider.directive.input input element}. - * - * @param {string} ng-model Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {number=} ng-minlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ng-maxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. - * @param {string=} ng-pattern Sets `pattern` validation error key if the value does not match the - * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for - * patterns defined as scope expressions. - * @param {string=} ng-change Angular expression to be executed when input changes due to user - * interaction with the input element. - */ - - -/** - * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.input - * @restrict E - * - * @description - * HTML input element control with angular data-binding. Input control follows HTML5 input types - * and polyfills the HTML5 validation behavior for older browsers. - * - * @param {string} ng-model Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {number=} ng-minlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ng-maxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. - * @param {string=} ng-pattern Sets `pattern` validation error key if the value does not match the - * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for - * patterns defined as scope expressions. - * @param {string=} ng-change Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.user = {name: 'guest', last: 'visitor'}; - } - </script> - <div ng-controller="Ctrl"> - <form name="myForm"> - User name: <input type="text" name="userName" ng-model="user.name" required> - <span class="error" ng-show="myForm.userName.$error.required"> - Required!</span><br> - Last name: <input type="text" name="lastName" ng-model="user.last" - ng-minlength="3" ng-maxlength="10"> - <span class="error" ng-show="myForm.lastName.$error.minlength"> - Too short!</span> - <span class="error" ng-show="myForm.lastName.$error.maxlength"> - Too long!</span><br> - </form> - <hr> - <tt>user = {{user}}</tt><br/> - <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br> - <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br> - <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br> - <tt>myForm.userName.$error = {{myForm.lastName.$error}}</tt><br> - <tt>myForm.$valid = {{myForm.$valid}}</tt><br> - <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br> - <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br> - <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br> - </div> - </doc:source> - <doc:scenario> - it('should initialize to model', function() { - expect(binding('user')).toEqual('{"last":"visitor","name":"guest"}'); - expect(binding('myForm.userName.$valid')).toEqual('true'); - expect(binding('myForm.$valid')).toEqual('true'); - }); - - it('should be invalid if empty when required', function() { - input('user.name').enter(''); - expect(binding('user')).toEqual('{"last":"visitor"}'); - expect(binding('myForm.userName.$valid')).toEqual('false'); - expect(binding('myForm.$valid')).toEqual('false'); - }); - - it('should be valid if empty when min length is set', function() { - input('user.last').enter(''); - expect(binding('user')).toEqual('{"last":"","name":"guest"}'); - expect(binding('myForm.lastName.$valid')).toEqual('true'); - expect(binding('myForm.$valid')).toEqual('true'); - }); - - it('should be invalid if less than required min length', function() { - input('user.last').enter('xx'); - expect(binding('user')).toEqual('{"name":"guest"}'); - expect(binding('myForm.lastName.$valid')).toEqual('false'); - expect(binding('myForm.lastName.$error')).toMatch(/minlength/); - expect(binding('myForm.$valid')).toEqual('false'); - }); - - it('should be invalid if longer than max length', function() { - input('user.last').enter('some ridiculously long name'); - expect(binding('user')) - .toEqual('{"name":"guest"}'); - expect(binding('myForm.lastName.$valid')).toEqual('false'); - expect(binding('myForm.lastName.$error')).toMatch(/maxlength/); - expect(binding('myForm.$valid')).toEqual('false'); - }); - </doc:scenario> - </doc:example> - */ -var inputDirective = [function() { - return { - restrict: 'E', - require: '?ngModel', - link: function(scope, element, attr, ctrl) { - if (ctrl) { - (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrl); - } - } - }; -}]; - -var VALID_CLASS = 'ng-valid', - INVALID_CLASS = 'ng-invalid', - PRISTINE_CLASS = 'ng-pristine', - DIRTY_CLASS = 'ng-dirty'; - -/** - * @ngdoc object - * @name angular.module.ng.$compileProvider.directive.ng-model.NgModelController - * - * @property {string} $viewValue Actual string value in the view. - * @property {*} $modelValue The value in the model, that the control is bound to. - * @property {Array.<Function>} $parsers Whenever the control reads value from the DOM, it executes - * all of these functions to sanitize / convert the value as well as validate. - * - * @property {Array.<Function>} $formatters Whenever the model value changes, it executes all of - * these functions to convert the value as well as validate. - * - * @property {Object} $error An bject hash with all errors as keys. - * - * @property {boolean} $pristine True if user has not interacted with the control yet. - * @property {boolean} $dirty True if user has already interacted with the control. - * @property {boolean} $valid True if there is no error. - * @property {boolean} $invalid True if at least one error on the control. - * - * @description - * - */ -var NgModelController = ['$scope', '$exceptionHandler', '$attrs', 'ngModel', '$element', - function($scope, $exceptionHandler, $attr, ngModel, $element) { - this.$viewValue = Number.NaN; - this.$modelValue = Number.NaN; - this.$parsers = []; - this.$formatters = []; - this.$viewChangeListeners = []; - this.$pristine = true; - this.$dirty = false; - this.$valid = true; - this.$invalid = false; - this.$render = noop; - this.$name = $attr.name; - - var parentForm = $element.inheritedData('$formController') || nullFormCtrl, - invalidCount = 0, // used to easily determine if we are valid - $error = this.$error = {}; // keep invalid keys here - - - // Setup initial state of the control - $element.addClass(PRISTINE_CLASS); - toggleValidCss(true); - - // convenience method for easy toggling of classes - function toggleValidCss(isValid, validationErrorKey) { - validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : ''; - $element. - removeClass((isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey). - addClass((isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey); - } - - /** - * @ngdoc function - * @name angular.module.ng.$compileProvider.directive.ng-model.NgModelController#$setValidity - * @methodOf angular.module.ng.$compileProvider.directive.ng-model.NgModelController - * - * @description - * Change the validity state, and notifies the form when the control changes validity. (i.e. it - * does not notify form if given validator is already marked as invalid). - * - * This method should be called by validators - i.e. the parser or formatter functions. - * - * @param {string} validationErrorKey Name of the validator. the `validationErrorKey` will assign - * to `$error[validationErrorKey]=isValid` so that it is available for data-binding. - * The `validationErrorKey` should be in camelCase and will get converted into dash-case - * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error` - * class and can be bound to as `{{someForm.someControl.$error.myError}}` . - * @param {boolean} isValid Whether the current state is valid (true) or invalid (false). - */ - this.$setValidity = function(validationErrorKey, isValid) { - if ($error[validationErrorKey] === !isValid) return; - - if (isValid) { - if ($error[validationErrorKey]) invalidCount--; - if (!invalidCount) { - toggleValidCss(true); - this.$valid = true; - this.$invalid = false; - } - } else { - toggleValidCss(false) - this.$invalid = true; - this.$valid = false; - invalidCount++; - } - - $error[validationErrorKey] = !isValid; - toggleValidCss(isValid, validationErrorKey); - - parentForm.$setValidity(validationErrorKey, isValid, this); - }; - - - /** - * @ngdoc function - * @name angular.module.ng.$compileProvider.directive.ng-model.NgModelController#$setViewValue - * @methodOf angular.module.ng.$compileProvider.directive.ng-model.NgModelController - * - * @description - * Read a value from view. - * - * This method should be called from within a DOM event handler. - * For example {@link angular.module.ng.$compileProvider.directive.input input} or - * {@link angular.module.ng.$compileProvider.directive.select select} directives call it. - * - * It internally calls all `formatters` and if resulted value is valid, updates the model and - * calls all registered change listeners. - * - * @param {string} value Value from the view. - */ - this.$setViewValue = function(value) { - this.$viewValue = value; - - // change to dirty - if (this.$pristine) { - this.$dirty = true; - this.$pristine = false; - $element.removeClass(PRISTINE_CLASS).addClass(DIRTY_CLASS); - parentForm.$setDirty(); - } - - forEach(this.$parsers, function(fn) { - value = fn(value); - }); - - if (this.$modelValue !== value) { - this.$modelValue = value; - ngModel(value); - forEach(this.$viewChangeListeners, function(listener) { - try { - listener(); - } catch(e) { - $exceptionHandler(e); - } - }) - } - }; - - // model -> value - var ctrl = this; - $scope.$watch(function() { - return ngModel(); - }, function(value) { - - // ignore change from view - if (ctrl.$modelValue === value) return; - - var formatters = ctrl.$formatters, - idx = formatters.length; - - ctrl.$modelValue = value; - while(idx--) { - value = formatters[idx](value); - } - - if (ctrl.$viewValue !== value) { - ctrl.$viewValue = value; - ctrl.$render(); - } - }); -}]; - - -/** - * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-model - * - * @element input - * - * @description - * Is directive that tells Angular to do two-way data binding. It works together with `input`, - * `select`, `textarea`. You can easily write your own directives to use `ng-model` as well. - * - * `ng-model` is responsible for: - * - * - binding the view into the model, which other directives such as `input`, `textarea` or `select` - * require, - * - providing validation behavior (i.e. required, number, email, url), - * - keeping state of the control (valid/invalid, dirty/pristine, validation errors), - * - setting related css class onto the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`), - * - register the control with parent {@link angular.module.ng.$compileProvider.directive.form form}. - * - * For basic examples, how to use `ng-model`, see: - * - * - {@link angular.module.ng.$compileProvider.directive.input input} - * - {@link angular.module.ng.$compileProvider.directive.input.text text} - * - {@link angular.module.ng.$compileProvider.directive.input.checkbox checkbox} - * - {@link angular.module.ng.$compileProvider.directive.input.radio radio} - * - {@link angular.module.ng.$compileProvider.directive.input.number number} - * - {@link angular.module.ng.$compileProvider.directive.input.email email} - * - {@link angular.module.ng.$compileProvider.directive.input.url url} - * - {@link angular.module.ng.$compileProvider.directive.select select} - * - {@link angular.module.ng.$compileProvider.directive.textarea textarea} - * - */ -var ngModelDirective = [function() { - return { - inject: { - ngModel: 'accessor' - }, - require: ['ngModel', '^?form'], - controller: NgModelController, - link: function(scope, element, attr, ctrls) { - // notify others, especially parent forms - - var modelCtrl = ctrls[0], - formCtrl = ctrls[1] || nullFormCtrl; - - formCtrl.$addControl(modelCtrl); - - element.bind('$destroy', function() { - formCtrl.$removeControl(modelCtrl); - }); - } - }; -}]; - - -/** - * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-change - * @restrict E - * - * @description - * Evaluate given expression when user changes the input. - * The expression is not evaluated when the value change is coming from the model. - * - * Note, this directive requires `ng-model` to be present. - * - * @element input - * - * @example - * <doc:example> - * <doc:source> - * <script> - * function Controller($scope) { - * $scope.counter = 0; - * $scope.change = function() { - * $scope.counter++; - * }; - * } - * </script> - * <div ng-controller="Controller"> - * <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" /> - * <input type="checkbox" ng-model="confirmed" id="ng-change-example2" /> - * <label for="ng-change-example2">Confirmed</label><br /> - * debug = {{confirmed}}<br /> - * counter = {{counter}} - * </div> - * </doc:source> - * <doc:scenario> - * it('should evaluate the expression if changing from view', function() { - * expect(binding('counter')).toEqual('0'); - * element('#ng-change-example1').click(); - * expect(binding('counter')).toEqual('1'); - * expect(binding('confirmed')).toEqual('true'); - * }); - * - * it('should not evaluate the expression if changing from model', function() { - * element('#ng-change-example2').click(); - * expect(binding('counter')).toEqual('0'); - * expect(binding('confirmed')).toEqual('true'); - * }); - * </doc:scenario> - * </doc:example> - */ -var ngChangeDirective = valueFn({ - require: 'ngModel', - link: function(scope, element, attr, ctrl) { - ctrl.$viewChangeListeners.push(function() { - scope.$eval(attr.ngChange); - }); - } -}); - - -/** - * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-model-instant - * - * @element input - * - * @description - * By default, Angular udpates the model only on `blur` event - when the input looses focus. - * If you want to update after every key stroke, use `ng-model-instant`. - * - * @example - * <doc:example> - * <doc:source> - * First name: <input type="text" ng-model="firstName" /><br /> - * Last name: <input type="text" ng-model="lastName" ng-model-instant /><br /> - * - * First name ({{firstName}}) is only updated on `blur` event, but the last name ({{lastName}}) - * is updated immediately, because of using `ng-model-instant`. - * </doc:source> - * <doc:scenario> - * it('should update first name on blur', function() { - * input('firstName').enter('santa', 'blur'); - * expect(binding('firstName')).toEqual('santa'); - * }); - * - * it('should update last name immediately', function() { - * input('lastName').enter('santa', 'keydown'); - * expect(binding('lastName')).toEqual('santa'); - * }); - * </doc:scenario> - * </doc:example> - */ -var ngModelInstantDirective = ['$browser', function($browser) { - return { - require: 'ngModel', - link: function(scope, element, attr, ctrl) { - var handler = function() { - scope.$apply(function() { - ctrl.$setViewValue(trim(element.val())); - }); - }; - - var timeout; - element.bind('keydown', function(event) { - var key = event.keyCode; - - // command modifiers arrows - if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return; - - if (!timeout) { - timeout = $browser.defer(function() { - handler(); - timeout = null; - }); - } - }); - - element.bind('change input', handler); - } - }; -}]; - - -var requiredDirective = [function() { - return { - require: '?ngModel', - link: function(scope, elm, attr, ctrl) { - if (!ctrl) return; - - var validator = function(value) { - if (attr.required && (isEmpty(value) || value === false)) { - ctrl.$setValidity('required', false); - return; - } else { - ctrl.$setValidity('required', true); - return value; - } - }; - - ctrl.$formatters.push(validator); - ctrl.$parsers.unshift(validator); - - attr.$observe('required', function() { - validator(ctrl.$viewValue); - }); - } - }; -}]; - - -/** - * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-list - * - * @description - * Text input that converts between comma-seperated string into an array of strings. - * - * @element input - * @param {string=} ng-list optional delimiter that should be used to split the value. If - * specified in form `/something/` then the value will be converted into a regular expression. - * - * @example - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.names = ['igor', 'misko', 'vojta']; - } - </script> - <form name="myForm" ng-controller="Ctrl"> - List: <input name="namesInput" ng-model="names" ng-list required> - <span class="error" ng-show="myForm.list.$error.required"> - Required!</span> - <tt>names = {{names}}</tt><br/> - <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/> - <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/> - <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> - <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> - </form> - </doc:source> - <doc:scenario> - it('should initialize to model', function() { - expect(binding('names')).toEqual('["igor","misko","vojta"]'); - expect(binding('myForm.namesInput.$valid')).toEqual('true'); - }); - - it('should be invalid if empty', function() { - input('names').enter(''); - expect(binding('names')).toEqual('[]'); - expect(binding('myForm.namesInput.$valid')).toEqual('false'); - }); - </doc:scenario> - </doc:example> - */ -var ngListDirective = function() { - return { - require: 'ngModel', - link: function(scope, element, attr, ctrl) { - var match = /\/(.*)\//.exec(attr.ngList), - separator = match && new RegExp(match[1]) || attr.ngList || ','; - - var parse = function(viewValue) { - var list = []; - - if (viewValue) { - forEach(viewValue.split(separator), function(value) { - if (value) list.push(trim(value)); - }); - } - - return list; - }; - - ctrl.$parsers.push(parse); - ctrl.$formatters.push(function(value) { - if (isArray(value) && !equals(parse(ctrl.$viewValue), value)) { - return value.join(', '); - } - - return undefined; - }); - } - }; -}; - - -var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/; - -var ngValueDirective = [function() { - return { - priority: 100, - compile: function(tpl, attr) { - if (CONSTANT_VALUE_REGEXP.test(attr.ngValue)) { - return function(scope) { - attr.$set('value', scope.$eval(attr.ngValue)); - }; - } else { - attr.$observers.value = []; - - return function(scope) { - scope.$watch(attr.ngValue, function(value) { - attr.$set('value', value, false); - }); - }; - } - } - }; -}]; diff --git a/src/directive/ngBind.js b/src/directive/ngBind.js deleted file mode 100644 index 32be2f4b..00000000 --- a/src/directive/ngBind.js +++ /dev/null @@ -1,155 +0,0 @@ -'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} ng-bind {@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" ng-model-instant><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} ng-bind-html-unsafe {@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} ng-bind-html {@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} ng-bind-template 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" ng-model-instant><br> - Name: <input type="text" ng-model="name" ng-model-instant><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); - }); - } -}]; diff --git a/src/directive/ngClass.js b/src/directive/ngClass.js deleted file mode 100644 index 21b75dd0..00000000 --- a/src/directive/ngClass.js +++ /dev/null @@ -1,143 +0,0 @@ -'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} ng-class {@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} ng-class-odd {@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} ng-class-even {@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 deleted file mode 100644 index 7422e15a..00000000 --- a/src/directive/ngCloak.js +++ /dev/null @@ -1,61 +0,0 @@ -'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 deleted file mode 100644 index 8e2c6f3b..00000000 --- a/src/directive/ngController.js +++ /dev/null @@ -1,103 +0,0 @@ -'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} ng-controller 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 = [function() { - return { - scope: true, - controller: '@' - }; -}]; diff --git a/src/directive/ngEventDirs.js b/src/directive/ngEventDirs.js deleted file mode 100644 index 74028fee..00000000 --- a/src/directive/ngEventDirs.js +++ /dev/null @@ -1,222 +0,0 @@ -'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} ng-click {@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} ng-dblclick {@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} ng-mousedown {@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} ng-mouseup {@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} ng-mouseover {@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} ng-mouseenter {@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} ng-mouseleave {@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} ng-mousemove {@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} ng-submit {@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 deleted file mode 100644 index 90fd0b40..00000000 --- a/src/directive/ngInclude.js +++ /dev/null @@ -1,131 +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} ng-include|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> - */ - - -/** - * @ngdoc event - * @name angular.module.ng.$compileProvider.directive.ng-include#$includeContentLoaded - * @eventOf angular.module.ng.$compileProvider.directive.ng-include - * @eventType emit on the current ng-include scope - * @description - * Emitted every time the ng-include content is reloaded. - */ -var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile', - function($http, $templateCache, $anchorScroll, $compile) { - return { - restrict: 'EA', - compile: function(element, attr) { - var srcExp = attr.ngInclude || attr.src, - scopeExp = attr.scope || '', - onloadExp = attr.onload || '', - 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('$includeContentLoaded'); - scope.$eval(onloadExp); - } - }).error(clearContent); - } else { - clearContent(); - } - }); - }; - } - } -}]; diff --git a/src/directive/ngInit.js b/src/directive/ngInit.js deleted file mode 100644 index cbd1b3ed..00000000 --- a/src/directive/ngInit.js +++ /dev/null @@ -1,37 +0,0 @@ -'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} ng-init {@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 deleted file mode 100644 index 2e9faa5a..00000000 --- a/src/directive/ngNonBindable.js +++ /dev/null @@ -1,33 +0,0 @@ -'use strict'; - -/** - * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-non-bindable - * @priority 1000 - * - * @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. - * - * @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, priority: 1000 }); diff --git a/src/directive/ngPluralize.js b/src/directive/ngPluralize.js deleted file mode 100644 index a8cc40a6..00000000 --- a/src/directive/ngPluralize.js +++ /dev/null @@ -1,204 +0,0 @@ -'use strict'; - -/** - * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-pluralize - * @restrict EA - * - * @description - * # Overview - * ng-pluralize is a directive 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 deleted file mode 100644 index 82f8b9c7..00000000 --- a/src/directive/ngRepeat.js +++ /dev/null @@ -1,181 +0,0 @@ -'use strict'; - -/** - * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-repeat - * - * @description - * The `ng-repeat` directive 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'` - * - * - * @element ANY - * @scope - * @priority 1000 - * @param {repeat_expression} ng-repeat 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 '" + - lhs + "'."); - } - 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 deleted file mode 100644 index 40a8a68e..00000000 --- a/src/directive/ngShowHide.js +++ /dev/null @@ -1,80 +0,0 @@ -'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} ng-show 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} ng-hide 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 deleted file mode 100644 index 8a4e7458..00000000 --- a/src/directive/ngStyle.js +++ /dev/null @@ -1,42 +0,0 @@ -'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} ng-style {@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 deleted file mode 100644 index 16b0c4d4..00000000 --- a/src/directive/ngSwitch.js +++ /dev/null @@ -1,112 +0,0 @@ -'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 {*} ng-switch|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/> - <div 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> - </div> - </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, - selectedScope; - - scope.$watch(watchExpr, function(value) { - if (selectedElement) { - selectedScope.$destroy(); - selectedElement.remove(); - selectedElement = selectedScope = null; - } - if ((selectedTransclude = cases['!' + value] || cases['?'])) { - scope.$eval(attr.change); - selectedScope = scope.$new(); - selectedTransclude(selectedScope, function(caseElement) { - selectedElement = caseElement; - element.append(caseElement); - }); - } - }); - }; - } -}); - -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 deleted file mode 100644 index ab4011f0..00000000 --- a/src/directive/ngTransclude.js +++ /dev/null @@ -1,58 +0,0 @@ -'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 deleted file mode 100644 index 95b1546d..00000000 --- a/src/directive/ngView.js +++ /dev/null @@ -1,170 +0,0 @@ -'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/> - - <div ng-view></div> - <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> - */ - - -/** - * @ngdoc event - * @name angular.module.ng.$compileProvider.directive.ng-view#$viewContentLoaded - * @eventOf angular.module.ng.$compileProvider.directive.ng-view - * @eventType emit on the current ng-view scope - * @description - * Emitted every time the ng-view content is reloaded. - */ -var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$compile', - '$controller', - function($http, $templateCache, $route, $anchorScroll, $compile, - $controller) { - return { - restrict: 'ECA', - terminal: true, - link: function(scope, element, attr) { - var changeCounter = 0, - lastScope, - onloadExp = attr.onload || ''; - - 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) { - element.contents(). - data('$ngControllerController', $controller(current.controller, {$scope: lastScope})); - } - - link(lastScope); - lastScope.$emit('$viewContentLoaded'); - lastScope.$eval(onloadExp); - - // $anchorScroll might listen on event... - $anchorScroll(); - } - }).error(clearContent); - } else { - clearContent(); - } - }); - } - }; -}]; diff --git a/src/directive/script.js b/src/directive/script.js deleted file mode 100644 index 4090ae24..00000000 --- a/src/directive/script.js +++ /dev/null @@ -1,43 +0,0 @@ -'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 - * @param {'text/ng-template'} type must be set to `'text/ng-template'` - * - * @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/directive/select.js b/src/directive/select.js deleted file mode 100644 index b70339fc..00000000 --- a/src/directive/select.js +++ /dev/null @@ -1,449 +0,0 @@ -'use strict'; - -/** - * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.select - * @restrict E - * - * @description - * HTML `SELECT` element with angular data-binding. - * - * # `ng-options` - * - * Optionally `ng-options` attribute can be used to dynamically generate a list of `<option>` - * elements for a `<select>` element using an array or an object obtained by evaluating the - * `ng-options` expression. - *˝˝ - * When an item in the select menu is select, the value of array element or object property - * represented by the selected option will be bound to the model identified by the `ng-model` attribute - * of the parent select element. - * - * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can - * be nested into the `<select>` element. This element will then represent `null` or "not selected" - * option. See example below for demonstration. - * - * Note: `ng-options` provides iterator facility for `<option>` element which must be used instead - * of {@link angular.module.ng.$compileProvider.directive.ng-repeat ng-repeat}. `ng-repeat` is not suitable for use with - * `<option>` element because of the following reasons: - * - * * value attribute of the option element that we need to bind to requires a string, but the - * source of data for the iteration might be in a form of array containing objects instead of - * strings - * * {@link angular.module.ng.$compileProvider.directive.ng-repeat ng-repeat} unrolls after the select binds causing - * incorect rendering on most browsers. - * * binding to a value not in list confuses most browsers. - * - * @param {string} name assignable expression to data-bind to. - * @param {string=} required The control is considered valid only if value is entered. - * @param {comprehension_expression=} ng-options in one of the following forms: - * - * * for array data sources: - * * `label` **`for`** `value` **`in`** `array` - * * `select` **`as`** `label` **`for`** `value` **`in`** `array` - * * `label` **`group by`** `group` **`for`** `value` **`in`** `array` - * * `select` **`as`** `label` **`group by`** `group` **`for`** `value` **`in`** `array` - * * for object data sources: - * * `label` **`for (`**`key` **`,`** `value`**`) in`** `object` - * * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object` - * * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object` - * * `select` **`as`** `label` **`group by`** `group` - * **`for` `(`**`key`**`,`** `value`**`) in`** `object` - * - * Where: - * - * * `array` / `object`: an expression which evaluates to an array / object to iterate over. - * * `value`: local variable which will refer to each item in the `array` or each property value - * of `object` during iteration. - * * `key`: local variable which will refer to a property name in `object` during iteration. - * * `label`: The result of this expression will be the label for `<option>` element. The - * `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`). - * * `select`: The result of this expression will be bound to the model of the parent `<select>` - * element. If not specified, `select` expression will default to `value`. - * * `group`: The result of this expression will be used to group options using the `<optgroup>` - * DOM element. - * - * @example - <doc:example> - <doc:source> - <script> - function MyCntrl($scope) { - $scope.colors = [ - {name:'black', shade:'dark'}, - {name:'white', shade:'light'}, - {name:'red', shade:'dark'}, - {name:'blue', shade:'dark'}, - {name:'yellow', shade:'light'} - ]; - $scope.color = $scope.colors[2]; // red - } - </script> - <div ng-controller="MyCntrl"> - <ul> - <li ng-repeat="color in colors"> - Name: <input ng-model="color.name"> - [<a href ng-click="colors.$remove(color)">X</a>] - </li> - <li> - [<a href ng-click="colors.push({})">add</a>] - </li> - </ul> - <hr/> - Color (null not allowed): - <select ng-model="color" ng-options="c.name for c in colors"></select><br> - - Color (null allowed): - <div class="nullable"> - <select ng-model="color" ng-options="c.name for c in colors"> - <option value="">-- chose color --</option> - </select> - </div><br/> - - Color grouped by shade: - <select ng-model="color" ng-options="c.name group by c.shade for c in colors"> - </select><br/> - - - Select <a href ng-click="color={name:'not in list'}">bogus</a>.<br> - <hr/> - Currently selected: {{ {selected_color:color} }} - <div style="border:solid 1px black; height:20px" - ng-style="{'background-color':color.name}"> - </div> - </div> - </doc:source> - <doc:scenario> - it('should check ng-options', function() { - expect(binding('{selected_color:color}')).toMatch('red'); - select('color').option('0'); - expect(binding('{selected_color:color}')).toMatch('black'); - using('.nullable').select('color').option(''); - expect(binding('{selected_color:color}')).toMatch('null'); - }); - </doc:scenario> - </doc:example> - */ - -var ngOptionsDirective = valueFn({ terminal: true }); -var selectDirective = ['$compile', '$parse', function($compile, $parse) { - //00001111100000000000222200000000000000000000003333000000000000044444444444444444000000000555555555555555550000000666666666666666660000000000000007777 - var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w\d]*)|(?:\(\s*([\$\w][\$\w\d]*)\s*,\s*([\$\w][\$\w\d]*)\s*\)))\s+in\s+(.*)$/; - - return { - restrict: 'E', - require: '?ngModel', - link: function(scope, element, attr, ctrl) { - if (!ctrl) return; - - var multiple = attr.multiple, - optionsExp = attr.ngOptions; - - // required validator - if (multiple && (attr.required || attr.ngRequired)) { - var requiredValidator = function(value) { - ctrl.$setValidity('required', !attr.required || (value && value.length)); - return value; - }; - - ctrl.$parsers.push(requiredValidator); - ctrl.$formatters.unshift(requiredValidator); - - attr.$observe('required', function() { - requiredValidator(ctrl.$viewValue); - }); - } - - if (optionsExp) Options(scope, element, ctrl); - else if (multiple) Multiple(scope, element, ctrl); - else Single(scope, element, ctrl); - - - //////////////////////////// - - - - function Single(scope, selectElement, ctrl) { - ctrl.$render = function() { - selectElement.val(ctrl.$viewValue); - }; - - selectElement.bind('change', function() { - scope.$apply(function() { - ctrl.$setViewValue(selectElement.val()); - }); - }); - } - - function Multiple(scope, selectElement, ctrl) { - var lastView; - ctrl.$render = function() { - var items = new HashMap(ctrl.$viewValue); - forEach(selectElement.children(), function(option) { - option.selected = isDefined(items.get(option.value)); - }); - }; - - // we have to do it on each watch since ng-model watches reference, but - // we need to work of an array, so we need to see if anything was inserted/removed - scope.$watch(function() { - if (!equals(lastView, ctrl.$viewValue)) { - lastView = copy(ctrl.$viewValue); - ctrl.$render(); - } - }); - - selectElement.bind('change', function() { - scope.$apply(function() { - var array = []; - forEach(selectElement.children(), function(option) { - if (option.selected) { - array.push(option.value); - } - }); - ctrl.$setViewValue(array); - }); - }); - } - - function Options(scope, selectElement, ctrl) { - var match; - - if (! (match = optionsExp.match(NG_OPTIONS_REGEXP))) { - throw Error( - "Expected ng-options in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" + - " but got '" + optionsExp + "'."); - } - - var displayFn = $parse(match[2] || match[1]), - valueName = match[4] || match[6], - keyName = match[5], - groupByFn = $parse(match[3] || ''), - valueFn = $parse(match[2] ? match[1] : valueName), - valuesFn = $parse(match[7]), - // we can't just jqLite('<option>') since jqLite is not smart enough - // to create it in <select> and IE barfs otherwise. - optionTemplate = jqLite(document.createElement('option')), - optGroupTemplate = jqLite(document.createElement('optgroup')), - nullOption = false, // if false then user will not be able to select it - // This is an array of array of existing option groups in DOM. We try to reuse these if possible - // optionGroupsCache[0] is the options with no option group - // optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element - optionGroupsCache = [[{element: selectElement, label:''}]]; - - // find existing special options - forEach(selectElement.children(), function(option) { - if (option.value == '') { - // developer declared null option, so user should be able to select it - nullOption = jqLite(option).remove(); - // compile the element since there might be bindings in it - $compile(nullOption)(scope); - } - }); - selectElement.html(''); // clear contents - - selectElement.bind('change', function() { - scope.$apply(function() { - var optionGroup, - collection = valuesFn(scope) || [], - locals = {}, - key, value, optionElement, index, groupIndex, length, groupLength; - - if (multiple) { - value = []; - for (groupIndex = 0, groupLength = optionGroupsCache.length; - groupIndex < groupLength; - groupIndex++) { - // list of options for that group. (first item has the parent) - optionGroup = optionGroupsCache[groupIndex]; - - for(index = 1, length = optionGroup.length; index < length; index++) { - if ((optionElement = optionGroup[index].element)[0].selected) { - key = optionElement.val(); - if (keyName) locals[keyName] = key; - locals[valueName] = collection[key]; - value.push(valueFn(scope, locals)); - } - } - } - } else { - key = selectElement.val(); - if (key == '?') { - value = undefined; - } else if (key == ''){ - value = null; - } else { - locals[valueName] = collection[key]; - if (keyName) locals[keyName] = key; - value = valueFn(scope, locals); - } - } - ctrl.$setViewValue(value); - }); - }); - - ctrl.$render = render; - - // TODO(vojta): can't we optimize this ? - scope.$watch(render); - - function render() { - var optionGroups = {'':[]}, // Temporary location for the option groups before we render them - optionGroupNames = [''], - optionGroupName, - optionGroup, - option, - existingParent, existingOptions, existingOption, - modelValue = ctrl.$modelValue, - values = valuesFn(scope) || [], - keys = keyName ? sortedKeys(values) : values, - groupLength, length, - groupIndex, index, - locals = {}, - selected, - selectedSet = false, // nothing is selected yet - lastElement, - element; - - if (multiple) { - selectedSet = new HashMap(modelValue); - } else if (modelValue === null || nullOption) { - // if we are not multiselect, and we are null then we have to add the nullOption - optionGroups[''].push({selected:modelValue === null, id:'', label:''}); - selectedSet = true; - } - - // We now build up the list of options we need (we merge later) - for (index = 0; length = keys.length, index < length; index++) { - locals[valueName] = values[keyName ? locals[keyName]=keys[index]:index]; - optionGroupName = groupByFn(scope, locals) || ''; - if (!(optionGroup = optionGroups[optionGroupName])) { - optionGroup = optionGroups[optionGroupName] = []; - optionGroupNames.push(optionGroupName); - } - if (multiple) { - selected = selectedSet.remove(valueFn(scope, locals)) != undefined; - } else { - selected = modelValue === valueFn(scope, locals); - selectedSet = selectedSet || selected; // see if at least one item is selected - } - optionGroup.push({ - id: keyName ? keys[index] : index, // either the index into array or key from object - label: displayFn(scope, locals) || '', // what will be seen by the user - selected: selected // determine if we should be selected - }); - } - if (!multiple && !selectedSet) { - // nothing was selected, we have to insert the undefined item - optionGroups[''].unshift({id:'?', label:'', selected:true}); - } - - // Now we need to update the list of DOM nodes to match the optionGroups we computed above - for (groupIndex = 0, groupLength = optionGroupNames.length; - groupIndex < groupLength; - groupIndex++) { - // current option group name or '' if no group - optionGroupName = optionGroupNames[groupIndex]; - - // list of options for that group. (first item has the parent) - optionGroup = optionGroups[optionGroupName]; - - if (optionGroupsCache.length <= groupIndex) { - // we need to grow the optionGroups - existingParent = { - element: optGroupTemplate.clone().attr('label', optionGroupName), - label: optionGroup.label - }; - existingOptions = [existingParent]; - optionGroupsCache.push(existingOptions); - selectElement.append(existingParent.element); - } else { - existingOptions = optionGroupsCache[groupIndex]; - existingParent = existingOptions[0]; // either SELECT (no group) or OPTGROUP element - - // update the OPTGROUP label if not the same. - if (existingParent.label != optionGroupName) { - existingParent.element.attr('label', existingParent.label = optionGroupName); - } - } - - lastElement = null; // start at the begining - for(index = 0, length = optionGroup.length; index < length; index++) { - option = optionGroup[index]; - if ((existingOption = existingOptions[index+1])) { - // reuse elements - lastElement = existingOption.element; - if (existingOption.label !== option.label) { - lastElement.text(existingOption.label = option.label); - } - if (existingOption.id !== option.id) { - lastElement.val(existingOption.id = option.id); - } - if (existingOption.element.selected !== option.selected) { - lastElement.prop('selected', (existingOption.selected = option.selected)); - } - } else { - // grow elements - - // if it's a null option - if (option.id === '' && nullOption) { - // put back the pre-compiled element - element = nullOption; - } else { - // jQuery(v1.4.2) Bug: We should be able to chain the method calls, but - // in this version of jQuery on some browser the .text() returns a string - // rather then the element. - (element = optionTemplate.clone()) - .val(option.id) - .attr('selected', option.selected) - .text(option.label); - } - - existingOptions.push(existingOption = { - element: element, - label: option.label, - id: option.id, - selected: option.selected - }); - if (lastElement) { - lastElement.after(element); - } else { - existingParent.element.append(element); - } - lastElement = element; - } - } - // remove any excessive OPTIONs in a group - index++; // increment since the existingOptions[0] is parent element not OPTION - while(existingOptions.length > index) { - existingOptions.pop().element.remove(); - } - } - // remove any excessive OPTGROUPs from select - while(optionGroupsCache.length > groupIndex) { - optionGroupsCache.pop()[0].element.remove(); - } - }; - } - } - } -}]; - -var optionDirective = ['$interpolate', function($interpolate) { - return { - restrict: 'E', - priority: 100, - compile: function(element, attr) { - if (isUndefined(attr.value)) { - var interpolateFn = $interpolate(element.text(), true); - if (interpolateFn) { - return function (scope, element, attr) { - scope.$watch(interpolateFn, function(value) { - attr.$set('value', value); - }); - } - } else { - attr.$set('value', element.text()); - } - } - } - } -}]; diff --git a/src/directive/style.js b/src/directive/style.js deleted file mode 100644 index 68ea1465..00000000 --- a/src/directive/style.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict'; - -var styleDirective = valueFn({ - restrict: 'E', - terminal: true -}); |
