diff --git a/Rakefile b/Rakefile index 069579e5e061..3d553beb104b 100644 --- a/Rakefile +++ b/Rakefile @@ -95,10 +95,88 @@ task :concat => :init do end +desc 'Minify source files into angular.min.js' +task :minify_angular => [:init] do + min_path = path_to('angular.min.js') + + angular_prefix = %x(cat src/angular.prefix) + angular_suffix = %x(cat src/angular.suffix) + + %x(java \ + #{java32flags()} \ + -jar lib/closure-compiler/compiler.jar \ + --compilation_level SIMPLE_OPTIMIZATIONS \ + --language_in ECMASCRIPT5_STRICT \ + --warning_level VERBOSE \ + --externs lib/externs/json.js \ + --jscomp_off nonStandardJsDocs \ + --jscomp_error ambiguousFunctionDecl \ + --jscomp_error checkRegExp \ + --jscomp_error checkTypes \ + --jscomp_error checkVars \ + --jscomp_error const \ + --jscomp_error constantProperty \ + --jscomp_error deprecated \ + --jscomp_error duplicateMessage \ + --jscomp_error es5Strict \ + --jscomp_error externsValidation \ + --jscomp_error globalThis \ + --jscomp_error internetExplorerChecks \ + --jscomp_error invalidCasts \ + --jscomp_error misplacedTypeAnnotation \ + --jscomp_error missingProperties \ + --jscomp_error suspiciousCode \ + --jscomp_error strictModuleDepCheck \ + --jscomp_error typeInvalidation \ + --jscomp_error undefinedNames \ + --jscomp_error undefinedVars \ + --jscomp_error unknownDefines \ + --jscomp_error uselessCode \ + --jscomp_error visibility \ + --output_wrapper '#{angular_prefix} %output% #{angular_suffix}' \ + --js #{files['angularSrc'].flatten.join(" \\\n --js ")} \ + --js_output_file #{min_path}) + + rewrite_file(min_path) do |content| + content.gsub!('"NG_VERSION_FULL"', NG_VERSION.full). + gsub!('"NG_VERSION_MAJOR"', NG_VERSION.major). + gsub!('"NG_VERSION_MINOR"', NG_VERSION.minor). + gsub!('"NG_VERSION_DOT"', NG_VERSION.dot). + gsub!('"NG_VERSION_CODENAME"', NG_VERSION.codename). + gsub!(/\s*['"]use strict['"];?\s*/, ''). # remove all file-specific strict mode flags + sub!(/\(function\([^)]*\)\s*\{/, "\\0\n'use strict';") # add single strict mode flag + end +end + + +desc 'Double-minify angular.min.js into angular.min.js' +task :minify_angular2 => [:minify_angular] do + # We double minify to rename free floating fns in angular closure, this will go away + # when we switch to namespaces + + input_path = path_to('angular.min.js') + output_path = path_to('angular.min2.js') + + + # TODO(i): remove rather than suppress bogus warnings + %x(java \ + #{java32flags()} \ + -jar lib/closure-compiler/compiler.jar \ + --compilation_level SIMPLE_OPTIMIZATIONS \ + --language_in ECMASCRIPT5_STRICT \ + --externs lib/externs/json.js \ + --jscomp_off suspiciousCode \ + --warning_level QUIET \ + --js #{input_path} \ + --js_output_file #{output_path}) + + FileUtils.cp output_path, input_path +end + + desc 'Minify JavaScript' -task :minify => [:init, :concat, :concat_scenario] do - [ 'angular.js', - 'angular-cookies.js', +task :minify => [:init, :concat, :concat_scenario, :minify_angular] do + [ 'angular-cookies.js', 'angular-loader.js', 'angular-resource.js', 'angular-sanitize.js', @@ -265,7 +343,7 @@ def gen_css(cssFile, minify = false) css.gsub! /'/, "\\\\'" css.gsub! /\n/, "\\n" - return %Q{angular.element(document).find('head').append('');} + return %Q{window.angular.element(document).find('head').append('');} end diff --git a/angularFiles.js b/angularFiles.js index 47f18961f736..3a426f5feb7d 100644 --- a/angularFiles.js +++ b/angularFiles.js @@ -59,7 +59,9 @@ angularFiles = { 'src/ng/directive/ngView.js', 'src/ng/directive/script.js', 'src/ng/directive/select.js', - 'src/ng/directive/style.js' + 'src/ng/directive/style.js', + + 'src/angular-bootstrap.js' ], 'angularSrcModules': [ @@ -127,7 +129,8 @@ angularFiles = { 'jstdExclude': [ 'test/jquery_alias.js', 'src/angular-bootstrap.js', - 'src/ngScenario/angular-bootstrap.js' + 'src/angular-dev.js', + 'src/ngScenario/angular-dev.js' ], 'jstdScenario': [ @@ -168,8 +171,9 @@ angularFiles = { ], 'jstdPerfExclude': [ - 'src/ng/angular-bootstrap.js', - 'src/ngScenario/angular-bootstrap.js' + 'src/angular-bootstrap.js', + 'src/angular-dev.js', + 'src/ngScenario/angular-dev.js' ], 'jstdJquery': [ @@ -190,7 +194,8 @@ angularFiles = { 'jstdJqueryExclude': [ 'src/angular-bootstrap.js', - 'src/ngScenario/angular-bootstrap.js', + 'src/angular-dev.js', + 'src/ngScenario/angular-dev.js', 'test/jquery_remove.js' ] }; diff --git a/check-size.sh b/check-size.sh index fd46d0c9227b..111b409ba939 100755 --- a/check-size.sh +++ b/check-size.sh @@ -1,5 +1,8 @@ #!/bin/bash -rake minify +#TODO(i): UNCOMMENT +#rake minify gzip -c < build/angular.min.js > build/angular.min.js.gzip -ls -l build/angular.min.* +gzip -c < build/angular.mmin.js > build/angular.mmin.js.gzip +gzip -c < build/angular.mmin2.js > build/angular.mmin2.js.gzip +ls -l build/angular.m* diff --git a/example/personalLog/personalLog.html b/example/personalLog/personalLog.html index 77d2ddc280e2..53eba7232fbe 100644 --- a/example/personalLog/personalLog.html +++ b/example/personalLog/personalLog.html @@ -7,7 +7,7 @@ setupModuleLoader(window); - + diff --git a/example/personalLog/personalLog.js b/example/personalLog/personalLog.js index c22b8702e4a6..5015cd6466a7 100644 --- a/example/personalLog/personalLog.js +++ b/example/personalLog/personalLog.js @@ -46,7 +46,7 @@ app.controller('LogCtrl', ['$cookieStore', '$scope', function LogCtrl($cookieSto /** * Persistently removes a log from logs. - * @param {object} log The log to remove. + * @param {Object} log The log to remove. */ $scope.rmLog = function(log) { for ( var i = 0; i < logs.length; i++) { diff --git a/example/personalLog/scenario/runner.html b/example/personalLog/scenario/runner.html index 298581d96bfd..73d0fa1b059c 100644 --- a/example/personalLog/scenario/runner.html +++ b/example/personalLog/scenario/runner.html @@ -3,7 +3,7 @@ Personal Log Scenario Runner - + diff --git a/example/temp.html b/example/temp.html index da92c68ccf3c..43e92deaae04 100644 --- a/example/temp.html +++ b/example/temp.html @@ -16,7 +16,7 @@ }; }); - +

diff --git a/example/tweeter/tweeter_addressbook.html b/example/tweeter/tweeter_addressbook.html index 5ffa6f74bb61..6c4f11c4432c 100644 --- a/example/tweeter/tweeter_addressbook.html +++ b/example/tweeter/tweeter_addressbook.html @@ -5,7 +5,7 @@ - + diff --git a/example/tweeter/tweeter_demo.html b/example/tweeter/tweeter_demo.html index 6966192a5114..593831046708 100644 --- a/example/tweeter/tweeter_demo.html +++ b/example/tweeter/tweeter_demo.html @@ -5,7 +5,7 @@ - + diff --git a/lib/closure-compiler/README b/lib/closure-compiler/README index ece717586fff..270cfc278ca5 100644 --- a/lib/closure-compiler/README +++ b/lib/closure-compiler/README @@ -146,13 +146,13 @@ the parse tree data structures were extracted and modified significantly for use by Google's JavaScript compiler. Local Modifications: The packages have been renamespaced. All code not -relavant to parsing has been removed. A JSDoc parser and static typing +relevant to parsing has been removed. A JsDoc parser and static typing system have been added. ----- Code in: -lib/libtrunk_rhino_parser_jarjared.jar +lib/rhino Rhino URL: http://www.mozilla.org/rhino @@ -161,9 +161,8 @@ License: Netscape Public License and MPL / GPL dual license Description: Mozilla Rhino is an implementation of JavaScript for the JVM. -Local Modifications: None. We've used JarJar to renamespace the code -post-compilation. See: -http://code.google.com/p/jarjar/ +Local Modifications: Minor changes to parsing JSDoc that usually get pushed +up-stream to Rhino trunk. ----- @@ -172,7 +171,7 @@ lib/args4j.jar Args4j URL: https://args4j.dev.java.net/ -Version: 2.0.12 +Version: 2.0.16 License: MIT Description: @@ -188,7 +187,7 @@ lib/guava.jar Guava Libraries URL: http://code.google.com/p/guava-libraries/ -Version: r08 +Version: 13.0.1 License: Apache License 2.0 Description: Google's core Java libraries. @@ -210,13 +209,28 @@ Description: Annotations for software defect detection. Local Modifications: None. +----- +Code in: +lib/jarjar.jar + +Jar Jar Links +URL: http://jarjar.googlecode.com/ +Version: 1.1 +License: Apache License 2.0 + +Description: +A utility for repackaging Java libraries. + +Local Modifications: None. + + ---- Code in: lib/junit.jar JUnit URL: http://sourceforge.net/projects/junit/ -Version: 4.8.2 +Version: 4.10 License: Common Public License 1.0 Description: A framework for writing and running automated tests in Java. @@ -230,7 +244,7 @@ lib/protobuf-java.jar Protocol Buffers URL: http://code.google.com/p/protobuf/ -Version: 2.3.0 +Version: 2.4.1 License: New BSD License Description: Supporting libraries for protocol buffers, @@ -267,9 +281,9 @@ Local Modifications: None --- Code in: -tools/maven-ant-tasks-2.1.1.jar +tools/maven-ant-tasks-2.1.3.jar URL: http://maven.apache.org -Version 2.1.1 +Version 2.1.3 License: Apache License 2.0 Description: Maven Ant tasks are used to manage dependencies and to install/deploy to diff --git a/lib/closure-compiler/compiler.jar b/lib/closure-compiler/compiler.jar index 694808c0cac6..059db41d2c3b 100644 Binary files a/lib/closure-compiler/compiler.jar and b/lib/closure-compiler/compiler.jar differ diff --git a/lib/closure-compiler/version.txt b/lib/closure-compiler/version.txt index df60031294fa..dfd519f07eba 100644 --- a/lib/closure-compiler/version.txt +++ b/lib/closure-compiler/version.txt @@ -1 +1,2 @@ -20110615 +20130207 +r4796bc81 diff --git a/lib/externs/json.js b/lib/externs/json.js new file mode 100644 index 000000000000..591d69bb133f --- /dev/null +++ b/lib/externs/json.js @@ -0,0 +1,45 @@ +/* + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Definitions for the JSON specification. + * @see http://www.json.org/json2.js. + * @externs + */ + +// This cannot go into the COMMON externs because it conflicts with the pure +// JavaScript implementations of the API. + +var JSON = {}; + +/** + * @param {string} jsonStr The string to parse. + * @param {(function(string, *) : *)=} opt_reviver + * @return {*} The JSON object. + * @throws {Error} + * @nosideeffects + */ +JSON.parse = function(jsonStr, opt_reviver) {}; + +/** + * @param {*} jsonObj Input object. + * @param {(Array.|(function(string, *) : *)|null)=} opt_replacer + * @param {(number|string)=} opt_space + * @return {string} JSON string which represents jsonObj. + * @throws {Error} + * @nosideeffects + */ +JSON.stringify = function(jsonObj, opt_replacer, opt_space) {}; diff --git a/src/Angular.js b/src/Angular.js index 27ef157d0091..44f19dfb97ca 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -59,38 +59,11 @@ var /** holds major version number for IE or NaN for real browsers */ /** @name angular */ angular = window.angular || (window.angular = {}), + ng = {}, //ng namespace for closure types angularModule, nodeName_, uid = ['0', '0', '0']; -/** - * @ngdoc function - * @name angular.forEach - * @function - * - * @description - * Invokes the `iterator` function once for each item in `obj` collection, which can be either an - * object or an array. The `iterator` function is invoked with `iterator(value, key)`, where `value` - * is the value of an object property or an array element and `key` is the object property key or - * array element index. Specifying a `context` for the function is optional. - * - * Note: this function was previously known as `angular.foreach`. - * -

-     var values = {name: 'misko', gender: 'male'};
-     var log = [];
-     angular.forEach(values, function(value, key){
-       this.push(key + ': ' + value);
-     }, log);
-     expect(log).toEqual(['name: misko', 'gender:male']);
-   
- * - * @param {Object|Array} obj Object to iterate over. - * @param {Function} iterator Iterator function. - * @param {Object=} context Object to become context (`this`) for the iterator function. - * @returns {Object|Array} Reference to `obj`. - */ - /** * @private @@ -114,6 +87,34 @@ function isArrayLike(obj) { } +/** + * @ngdoc function + * @name angular.forEach + * @function + * + * @description + * Invokes the `iterator` function once for each item in `obj` collection, which can be either an + * object or an array. The `iterator` function is invoked with `iterator(value, key)`, where `value` + * is the value of an object property or an array element and `key` is the object property key or + * array element index. Specifying a `context` for the function is optional. + * + * Note: this function was previously known as `angular.foreach`. + * +
+ var values = {name: 'misko', gender: 'male'};
+ var log = [];
+ angular.forEach(values, function(value, key){
+       this.push(key + ': ' + value);
+     }, log);
+ expect(log).toEqual(['name: misko', 'gender:male']);
+ 
+ * + * @template T + * @param {Object.|Array.|undefined} obj Object to iterate over. + * @param {function(T, (string|number))} iterator Iterator function. + * @param {Object=} context Object to become context (`this`) for the iterator function. + * @returns {Object.|Array.|undefined} Returns the input `obj`. + */ function forEach(obj, iterator, context) { var key; if (obj) { @@ -149,6 +150,14 @@ function sortedKeys(obj) { return keys.sort(); } + +/** + * @template T + * @param {Object.} obj + * @param {function(T, string)} iterator + * @param {Object=} context + * @return {Array.} sorted array of keys + */ function forEachSorted(obj, iterator, context) { var keys = sortedKeys(obj); for ( var i = 0; i < keys.length; i++) { @@ -160,8 +169,10 @@ function forEachSorted(obj, iterator, context) { /** * when using forEach the params are value, key, but it is often useful to have key, value. - * @param {function(string, *)} iteratorFn - * @returns {function(*, string)} + * + * @template T1, T2 + * @param {function(T1, T2)} iteratorFn + * @returns {function(T2, T1)} */ function reverseParams(iteratorFn) { return function(value, key) { iteratorFn(key, value) }; @@ -173,7 +184,7 @@ function reverseParams(iteratorFn) { * the number string gets longer over time, and it can also overflow, where as the nextId * will grow much slower, it is a string, and it will never overflow. * - * @returns an unique alpha-numeric string + * @returns {string} A unique alpha-numeric string */ function nextUid() { var index = uid.length; @@ -206,10 +217,10 @@ function nextUid() { * Extends the destination object `dst` by copying all of the properties from the `src` object(s) * to `dst`. You can specify multiple `src` objects. * - * @param {Object} dst Destination object. - * @param {...Object} src Source object(s). + * @param {!Object} dst Destination object. + * @param {...Object} var_args Source object(s). */ -function extend(dst) { +function extend(dst, var_args) { forEach(arguments, function(obj){ if (obj !== dst) { forEach(obj, function(value, key){ @@ -225,6 +236,11 @@ function int(str) { } +/** + * @param {Object} parent + * @param {Object=} extra + * @return {*} + */ function inherit(parent, extra) { return extend(new (extend(function() {}, {prototype:parent}))(), extra); } @@ -390,12 +406,11 @@ function isFunction(value){return typeof value == 'function';} /** * Checks if `obj` is a window object. * - * @private * @param {*} obj Object to check * @returns {boolean} True if `obj` is a window obj. */ function isWindow(obj) { - return obj && obj.document && obj.location && obj.alert && obj.setInterval; + return !!(obj && obj.document && obj.location && obj.alert && obj.setInterval); } @@ -426,18 +441,18 @@ function trim(value) { * @description * Determines if a reference is a DOM element (or wrapped jQuery element). * - * @param {*} value Reference to check. + * @param {*} node Reference to check. * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element). */ function isElement(node) { - return node && + return !!(node && (node.nodeName // we are a direct element - || (node.bind && node.find)); // we have a bind and find method part of jQuery API + || (node.bind && node.find))); // we have a bind and find method part of jQuery API } /** * @param str 'key1,key2,...' - * @returns {object} in the form of {key1:true, key2:true, ...} + * @returns {Object} in the form of {key1:true, key2:true, ...} */ function makeMap(str){ var obj = {}, items = str.split(","), i; @@ -460,10 +475,17 @@ if (msie < 9) { } +/** + * @template T + * @param {!(Array.|Object.)} obj + * @param {!function(T, (string|number))} iterator + * @param {Object=} context + * @return {!Array} + */ function map(obj, iterator, context) { var results = []; - forEach(obj, function(value, index, list) { - results.push(iterator.call(context, value, index, list)); + forEach(obj, function(value, index) { + results.push(iterator.call(context, value, index)); }); return results; } @@ -544,25 +566,26 @@ function isLeafNode (node) { * Note: this function is used to augment the Object type in Angular expressions. See * {@link ng.$filter} for more information about Angular arrays. * - * @param {*} source The source that will be used to make a copy. + * @template T + * @param {!T} source The source that will be used to make a copy. * Can be any type, including primitives, `null`, and `undefined`. * @param {(Object|Array)=} destination Destination into which the source is copied. If * provided, must be of the same type as `source`. - * @returns {*} The copy or updated `destination`, if `destination` was specified. + * @returns {!T} The copy or updated `destination`, if `destination` was specified. */ function copy(source, destination){ if (isWindow(source) || isScope(source)) throw Error("Can't copy Window or Scope"); if (!destination) { - destination = source; if (source) { if (isArray(source)) { - destination = copy(source, []); + return copy(source, []); } else if (isDate(source)) { - destination = new Date(source.getTime()); + return new Date(source.getTime()); } else if (isObject(source)) { - destination = copy(source, {}); + return copy(source, {}); } } + return source; } else { if (source === destination) throw Error("Can't copy equivalent objects or arrays"); if (isArray(source)) { @@ -676,15 +699,15 @@ function sliceArgs(args, startIndex) { * * @description * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for - * `fn`). You can supply optional `args` that are are prebound to the function. This feature is also + * `fn`). You can supply optional `var_args` that are are prebound to the function. This feature is also * known as [function currying](http://en.wikipedia.org/wiki/Currying). * * @param {Object} self Context which `fn` should be evaluated in. * @param {function()} fn Function to be bound. - * @param {...*} args Optional arguments to be prebound to the `fn` function call. + * @param {...*} var_args Optional arguments to be prebound to the `fn` function call. * @returns {function()} Function that wraps the `fn` with all the specified bindings. */ -function bind(self, fn) { +function bind(self, fn, var_args) { var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : []; if (isFunction(fn) && !(fn instanceof RegExp)) { return curryArgs.length @@ -730,12 +753,12 @@ function toJsonReplacer(key, value) { * @description * Serializes input into a JSON-formatted string. * - * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON. + * @param {*} obj Input to be serialized into JSON. * @param {boolean=} pretty If set to true, the JSON output will contain newlines and whitespace. - * @returns {string} Jsonified string representing `obj`. + * @returns {string|undefined} Jsonified string representing `obj`. */ function toJson(obj, pretty) { - return JSON.stringify(obj, toJsonReplacer, pretty ? ' ' : null); + return JSON.stringify(obj, toJsonReplacer, pretty ? ' ' : undefined); } @@ -748,11 +771,11 @@ function toJson(obj, pretty) { * Deserializes a JSON string. * * @param {string} json JSON string to deserialize. - * @returns {Object|Array|Date|string|number} Deserialized thingy. + * @returns {Object|Array|string|number} Deserialized thingy. */ function fromJson(json) { return isString(json) - ? JSON.parse(json) + ? /** @type {Object|Array|string|number}*/(JSON.parse(json)) : json; } @@ -787,15 +810,18 @@ function startingTag(element) { /** * Parses an escaped url query string into key-value pairs. + * + * @param {string} keyValueString * @returns Object.<(string|boolean)> */ -function parseKeyValue(/**string*/keyValue) { - var obj = {}, key_value, key; - forEach((keyValue || "").split('&'), function(keyValue){ +function parseKeyValue(keyValueString) { + var obj = {}; + var key_value, key; + forEach((keyValueString || "").split('&'), function(keyValue){ if (keyValue) { - key_value = keyValue.split('='); - key = decodeURIComponent(key_value[0]); - obj[key] = isDefined(key_value[1]) ? decodeURIComponent(key_value[1]) : true; + keyValue = keyValue.split('='); + key = decodeURIComponent(keyValue[0]); + obj[key] = isDefined(keyValue[1]) ? decodeURIComponent(keyValue[1]) : true; } }); return obj; @@ -855,11 +881,9 @@ function encodeUriQuery(val, pctEncodeSpaces) { * @name ng.directive:ngApp * * @element ANY - * @param {angular.Module} ngApp an optional application - * {@link angular.module module} name to load. + * @param {string} ngApp an optional application {@link angular.module module} name to load. * * @description - * * Use this directive to auto-bootstrap on application. Only * one directive can be used per HTML document. The directive * designates the root of the application and is typically placed @@ -878,6 +902,11 @@ function encodeUriQuery(val, pctEncodeSpaces) { * */ + +/** + * @param {!(Element|Document)} element + * @param {!function(!Element, Array.)} bootstrap + */ function angularInit(element, bootstrap) { var elements = [element], appElement, @@ -930,9 +959,9 @@ function angularInit(element, bootstrap) { * * See: {@link guide/bootstrap Bootstrap} * - * @param {Element} element DOM element which is the root of angular application. - * @param {Array=} modules an array of module declarations. See: {@link angular.module modules} - * @returns {AUTO.$injector} Returns the newly created injector for this app. + * @param {!Element} element DOM element which is the root of angular application. + * @param {Array.=} modules an array of module declarations. See: {@link angular.module modules} + * @returns {ng.Injector} Returns the newly created injector for this app. */ function bootstrap(element, modules) { element = jqLite(element); @@ -984,6 +1013,9 @@ function bindJQuery() { /** * throw error of the argument is falsy. + * @param {*} arg + * @param {string=} name + * @param {string=} reason */ function assertArg(arg, name, reason) { if (!arg) { @@ -992,7 +1024,12 @@ function assertArg(arg, name, reason) { return arg; } -function assertArgFn(arg, name, acceptArrayAnnotation) { +/** + * @param {*} arg + * @param {string} name + * @param {boolean=} acceptArrayAnnotation + */ + function assertArgFn(arg, name, acceptArrayAnnotation) { if (acceptArrayAnnotation && isArray(arg)) { arg = arg[arg.length - 1]; } diff --git a/src/angular-bootstrap.js b/src/angular-bootstrap.js index 86b958148c40..03ba8d888d57 100644 --- a/src/angular-bootstrap.js +++ b/src/angular-bootstrap.js @@ -1,227 +1,11 @@ +'use strict'; -/*! - * $script.js Async loader & dependency manager - * https://github.com/ded/script.js - * (c) Dustin Diaz, Jacob Thornton 2011 - * License: MIT - */ -(function (name, definition, context) { - if (typeof context['module'] != 'undefined' && context['module']['exports']) context['module']['exports'] = definition() - else if (typeof context['define'] != 'undefined' && context['define'] == 'function' && context['define']['amd']) define(name, definition) - else context[name] = definition() -})('$script', function () { - var doc = document - , head = doc.getElementsByTagName('head')[0] - , validBase = /^https?:\/\// - , list = {}, ids = {}, delay = {}, scriptpath - , scripts = {}, s = 'string', f = false - , push = 'push', domContentLoaded = 'DOMContentLoaded', readyState = 'readyState' - , addEventListener = 'addEventListener', onreadystatechange = 'onreadystatechange' +//try to bind to jquery now so that one can write angular.element().read() +//but we will rebind on bootstrap again. +bindJQuery(); - function every(ar, fn) { - for (var i = 0, j = ar.length; i < j; ++i) if (!fn(ar[i])) return f - return 1 - } - function each(ar, fn) { - every(ar, function(el) { - return !fn(el) - }) - } - - if (!doc[readyState] && doc[addEventListener]) { - doc[addEventListener](domContentLoaded, function fn() { - doc.removeEventListener(domContentLoaded, fn, f) - doc[readyState] = 'complete' - }, f) - doc[readyState] = 'loading' - } - - function $script(paths, idOrDone, optDone) { - paths = paths[push] ? paths : [paths] - var idOrDoneIsDone = idOrDone && idOrDone.call - , done = idOrDoneIsDone ? idOrDone : optDone - , id = idOrDoneIsDone ? paths.join('') : idOrDone - , queue = paths.length - function loopFn(item) { - return item.call ? item() : list[item] - } - function callback() { - if (!--queue) { - list[id] = 1 - done && done() - for (var dset in delay) { - every(dset.split('|'), loopFn) && !each(delay[dset], loopFn) && (delay[dset] = []) - } - } - } - setTimeout(function () { - each(paths, function (path) { - if (scripts[path]) { - id && (ids[id] = 1) - return scripts[path] == 2 && callback() - } - scripts[path] = 1 - id && (ids[id] = 1) - create(!validBase.test(path) && scriptpath ? scriptpath + path + '.js' : path, callback) - }) - }, 0) - return $script - } - - function create(path, fn) { - var el = doc.createElement('script') - , loaded = f - el.onload = el.onerror = el[onreadystatechange] = function () { - if ((el[readyState] && !(/^c|loade/.test(el[readyState]))) || loaded) return; - el.onload = el[onreadystatechange] = null - loaded = 1 - scripts[path] = 2 - fn() - } - el.async = 1 - el.src = path - head.insertBefore(el, head.firstChild) - } - - $script.get = create - - $script.order = function (scripts, id, done) { - (function callback(s) { - s = scripts.shift() - if (!scripts.length) $script(s, id, done) - else $script(s, callback) - }()) - } - - $script.path = function (p) { - scriptpath = p - } - $script.ready = function (deps, ready, req) { - deps = deps[push] ? deps : [deps] - var missing = []; - !each(deps, function (dep) { - list[dep] || missing[push](dep); - }) && every(deps, function (dep) {return list[dep]}) ? - ready() : !function (key) { - delay[key] = delay[key] || [] - delay[key][push](ready) - req && req(missing) - }(deps.join('|')) - return $script - } - return $script -}, this); - - -/** - * @license AngularJS - * (c) 2010-2012 Google, Inc. http://angularjs.org - * License: MIT - */ -(function(window, document) { - - var filename = /^(.*\/)angular-bootstrap.js(#.*)?$/, - scripts = document.getElementsByTagName("SCRIPT"), - serverPath, - match, - globalVars = {}, - IGNORE = { - innerHeight: true, innerWidth: true, - onkeyup: true, onkeydown: true, onresize: true, - event: true, frames: true, external: true, - sessionStorage: true, clipboardData: true, localStorage: true}; - - for(var j = 0; j < scripts.length; j++) { - match = (scripts[j].src || "").match(filename); - if (match) { - serverPath = match[1]; - } - } - - document.write(''); - - $script.path(serverPath+'../'); - $script('angularFiles', function() { - var index = 0, - scripts = angularFiles.angularSrc; - - try { delete window.angularFiles; } catch(e) { window.angularFiles = undefined; } - // initialize the window property cache - for (var prop in window) { - if (IGNORE[prop] || prop.match(/^moz[A-Z]/)) { //skip special variables which keep on changing - continue; - } - try { - globalVars[key(prop)] = window[prop]; - } catch(e) {} //ignore properties that throw exception when accessed (common in FF) - } - - (function next() { - if (index < scripts.length) { - var file = scripts[index++], - last = index == scripts.length, - name = last ? 'angular' : file; - - $script(file.replace(/\.js$/, ''), name, function() { - angularClobberTest(file); - next(); - }); - } else { - // empty the cache to prevent mem leaks - globalVars = {}; - - bindJQuery(); - publishExternalAPI(window.angular); - - angularInit(document, angular.bootstrap); - } - })(); - }); - - function key(prop) { - return "ng-clobber_" + prop; - } - - function angularClobberTest(file) { - var varKey, prop, - clobbered = {}; - - for (prop in window) { - varKey = key(prop); - - if (IGNORE[prop] || prop.match(/^moz[A-Z]/)) { //skip special variables which keep on changing - continue; - } else if (!globalVars.hasOwnProperty(varKey)) { - //console.log('new global variable found: ', prop); - try { - globalVars[varKey] = window[prop]; - } catch(e) {} //ignore properties that throw exception when accessed (common in FF) - } else if (globalVars[varKey] !== window[prop] && !isActuallyNaN(window[prop]) && prop != 'jqLite') { - clobbered[prop] = true; - console.error("Global variable clobbered by script " + file + "! Variable name: " + prop); - globalVars[varKey] = window[prop]; - } - } - for (varKey in globalVars) { - prop = varKey.substr(11); - if (prop === 'event' || prop.match(/^moz[A-Z]/)) { //skip special variables which keep on changing - continue; - } - if (!clobbered[prop] && - prop != 'event' && - prop != 'jqLite' && - !isActuallyNaN(globalVars[varKey]) && - globalVars[varKey] !== window[prop]) { - - delete globalVars[varKey]; - console.warn("Global variable unexpectedly deleted in script " + file + "! " + - "Variable name: " + prop); - } - } - - function isActuallyNaN(val) { - return (typeof val === 'number') && isNaN(val); - } - }; -})(window, document); +publishExternalAPI(angular); +jqLite(document).ready(function() { + angularInit(document, bootstrap); +}); diff --git a/src/angular-dev.js b/src/angular-dev.js new file mode 100644 index 000000000000..86b958148c40 --- /dev/null +++ b/src/angular-dev.js @@ -0,0 +1,227 @@ + +/*! + * $script.js Async loader & dependency manager + * https://github.com/ded/script.js + * (c) Dustin Diaz, Jacob Thornton 2011 + * License: MIT + */ +(function (name, definition, context) { + if (typeof context['module'] != 'undefined' && context['module']['exports']) context['module']['exports'] = definition() + else if (typeof context['define'] != 'undefined' && context['define'] == 'function' && context['define']['amd']) define(name, definition) + else context[name] = definition() +})('$script', function () { + var doc = document + , head = doc.getElementsByTagName('head')[0] + , validBase = /^https?:\/\// + , list = {}, ids = {}, delay = {}, scriptpath + , scripts = {}, s = 'string', f = false + , push = 'push', domContentLoaded = 'DOMContentLoaded', readyState = 'readyState' + , addEventListener = 'addEventListener', onreadystatechange = 'onreadystatechange' + + function every(ar, fn) { + for (var i = 0, j = ar.length; i < j; ++i) if (!fn(ar[i])) return f + return 1 + } + function each(ar, fn) { + every(ar, function(el) { + return !fn(el) + }) + } + + if (!doc[readyState] && doc[addEventListener]) { + doc[addEventListener](domContentLoaded, function fn() { + doc.removeEventListener(domContentLoaded, fn, f) + doc[readyState] = 'complete' + }, f) + doc[readyState] = 'loading' + } + + function $script(paths, idOrDone, optDone) { + paths = paths[push] ? paths : [paths] + var idOrDoneIsDone = idOrDone && idOrDone.call + , done = idOrDoneIsDone ? idOrDone : optDone + , id = idOrDoneIsDone ? paths.join('') : idOrDone + , queue = paths.length + function loopFn(item) { + return item.call ? item() : list[item] + } + function callback() { + if (!--queue) { + list[id] = 1 + done && done() + for (var dset in delay) { + every(dset.split('|'), loopFn) && !each(delay[dset], loopFn) && (delay[dset] = []) + } + } + } + setTimeout(function () { + each(paths, function (path) { + if (scripts[path]) { + id && (ids[id] = 1) + return scripts[path] == 2 && callback() + } + scripts[path] = 1 + id && (ids[id] = 1) + create(!validBase.test(path) && scriptpath ? scriptpath + path + '.js' : path, callback) + }) + }, 0) + return $script + } + + function create(path, fn) { + var el = doc.createElement('script') + , loaded = f + el.onload = el.onerror = el[onreadystatechange] = function () { + if ((el[readyState] && !(/^c|loade/.test(el[readyState]))) || loaded) return; + el.onload = el[onreadystatechange] = null + loaded = 1 + scripts[path] = 2 + fn() + } + el.async = 1 + el.src = path + head.insertBefore(el, head.firstChild) + } + + $script.get = create + + $script.order = function (scripts, id, done) { + (function callback(s) { + s = scripts.shift() + if (!scripts.length) $script(s, id, done) + else $script(s, callback) + }()) + } + + $script.path = function (p) { + scriptpath = p + } + $script.ready = function (deps, ready, req) { + deps = deps[push] ? deps : [deps] + var missing = []; + !each(deps, function (dep) { + list[dep] || missing[push](dep); + }) && every(deps, function (dep) {return list[dep]}) ? + ready() : !function (key) { + delay[key] = delay[key] || [] + delay[key][push](ready) + req && req(missing) + }(deps.join('|')) + return $script + } + return $script +}, this); + + +/** + * @license AngularJS + * (c) 2010-2012 Google, Inc. http://angularjs.org + * License: MIT + */ +(function(window, document) { + + var filename = /^(.*\/)angular-bootstrap.js(#.*)?$/, + scripts = document.getElementsByTagName("SCRIPT"), + serverPath, + match, + globalVars = {}, + IGNORE = { + innerHeight: true, innerWidth: true, + onkeyup: true, onkeydown: true, onresize: true, + event: true, frames: true, external: true, + sessionStorage: true, clipboardData: true, localStorage: true}; + + for(var j = 0; j < scripts.length; j++) { + match = (scripts[j].src || "").match(filename); + if (match) { + serverPath = match[1]; + } + } + + document.write(''); + + $script.path(serverPath+'../'); + $script('angularFiles', function() { + var index = 0, + scripts = angularFiles.angularSrc; + + try { delete window.angularFiles; } catch(e) { window.angularFiles = undefined; } + // initialize the window property cache + for (var prop in window) { + if (IGNORE[prop] || prop.match(/^moz[A-Z]/)) { //skip special variables which keep on changing + continue; + } + try { + globalVars[key(prop)] = window[prop]; + } catch(e) {} //ignore properties that throw exception when accessed (common in FF) + } + + (function next() { + if (index < scripts.length) { + var file = scripts[index++], + last = index == scripts.length, + name = last ? 'angular' : file; + + $script(file.replace(/\.js$/, ''), name, function() { + angularClobberTest(file); + next(); + }); + } else { + // empty the cache to prevent mem leaks + globalVars = {}; + + bindJQuery(); + publishExternalAPI(window.angular); + + angularInit(document, angular.bootstrap); + } + })(); + }); + + function key(prop) { + return "ng-clobber_" + prop; + } + + function angularClobberTest(file) { + var varKey, prop, + clobbered = {}; + + for (prop in window) { + varKey = key(prop); + + if (IGNORE[prop] || prop.match(/^moz[A-Z]/)) { //skip special variables which keep on changing + continue; + } else if (!globalVars.hasOwnProperty(varKey)) { + //console.log('new global variable found: ', prop); + try { + globalVars[varKey] = window[prop]; + } catch(e) {} //ignore properties that throw exception when accessed (common in FF) + } else if (globalVars[varKey] !== window[prop] && !isActuallyNaN(window[prop]) && prop != 'jqLite') { + clobbered[prop] = true; + console.error("Global variable clobbered by script " + file + "! Variable name: " + prop); + globalVars[varKey] = window[prop]; + } + } + for (varKey in globalVars) { + prop = varKey.substr(11); + if (prop === 'event' || prop.match(/^moz[A-Z]/)) { //skip special variables which keep on changing + continue; + } + if (!clobbered[prop] && + prop != 'event' && + prop != 'jqLite' && + !isActuallyNaN(globalVars[varKey]) && + globalVars[varKey] !== window[prop]) { + + delete globalVars[varKey]; + console.warn("Global variable unexpectedly deleted in script " + file + "! " + + "Variable name: " + prop); + } + } + + function isActuallyNaN(val) { + return (typeof val === 'number') && isNaN(val); + } + }; +})(window, document); + diff --git a/src/angular.suffix b/src/angular.suffix index c86200bb31f4..1292c703ec0e 100644 --- a/src/angular.suffix +++ b/src/angular.suffix @@ -1,11 +1 @@ - //try to bind to jquery now so that one can write angular.element().read() - //but we will rebind on bootstrap again. - bindJQuery(); - - publishExternalAPI(angular); - - jqLite(document).ready(function() { - angularInit(document, bootstrap); - }); - })(window, document); diff --git a/src/apis.js b/src/apis.js index 0e94e2a55ce2..d041f0815c15 100644 --- a/src/apis.js +++ b/src/apis.js @@ -33,10 +33,14 @@ function hashKey(obj) { /** * HashMap which can use objects as keys + * + * @constructor + * @param {(Object|Array)=} initMap Map of key/value data for HashMap initialization. */ -function HashMap(array){ - forEach(array, this.put, this); +function HashMap(initMap) { + forEach(initMap, this.put, this); } + HashMap.prototype = { /** * Store key value pair @@ -68,9 +72,10 @@ HashMap.prototype = { /** * A map where multiple values can be added to the same key such that they form a queue. - * @returns {HashQueueMap} + * @constructor */ function HashQueueMap() {} + HashQueueMap.prototype = { /** * Same as array push, but using an array as the value for the hash diff --git a/src/auto/injector.js b/src/auto/injector.js index 70160fc566b4..9046f8e8a4ea 100644 --- a/src/auto/injector.js +++ b/src/auto/injector.js @@ -144,7 +144,7 @@ function annotate(fn) { * @description * Invoke the method and supply the method arguments from the `$injector`. * - * @param {!function} fn The function to invoke. The function arguments come form the function annotation. + * @param {!Function} fn The function to invoke. The function arguments come form the function annotation. * @param {Object=} self The `this` for the invoked method. * @param {Object=} locals Optional object. If preset then any argument names are read from this object first, before * the `$injector` is consulted. @@ -159,7 +159,7 @@ function annotate(fn) { * Create a new instance of JS type. The method takes a constructor function invokes the new operator and supplies * all of the arguments to the constructor function as specified by the constructor annotation. * - * @param {function} Type Annotated constructor function. + * @param {!Function} Type Annotated constructor function. * @param {Object=} locals Optional object. If preset then any argument names are read from this object first, before * the `$injector` is consulted. * @returns {Object} new instance of `Type`. @@ -238,7 +238,7 @@ function annotate(fn) { * ).toEqual(['$compile', '$rootScope']); * * - * @param {function|Array.} fn Function for which dependent service names need to be retrieved as described + * @param {Function|Array.} fn Function for which dependent service names need to be retrieved as described * above. * * @returns {Array.} The names of the services which the function requires. @@ -394,7 +394,10 @@ function annotate(fn) { * decorated or delegated to. */ - +/** + * @param {Array.} modulesToLoad + * @return {ng.Injector} + */ function createInjector(modulesToLoad) { var INSTANTIATING = {}, providerSuffix = 'Provider', @@ -417,7 +420,7 @@ function createInjector(modulesToLoad) { instanceCache = {}, instanceInjector = (instanceCache.$injector = createInternalInjector(instanceCache, function(servicename) { - var provider = providerInjector.get(servicename + providerSuffix); + var provider = /** @type {!ng.Provider} */(providerInjector.get(servicename + providerSuffix)); return instanceInjector.invoke(provider.$get, provider); })); @@ -430,6 +433,11 @@ function createInjector(modulesToLoad) { // $provider //////////////////////////////////// + /** + * @template T + * @param {function(...[?]):T} delegate + * @return {function((string|Object), *):(T|undefined)} + */ function supportObject(delegate) { return function(key, value) { if (isObject(key)) { @@ -440,14 +448,21 @@ function createInjector(modulesToLoad) { } } + /** + * @param {!string} name + * @param {!(ng.Provider|Function|Array)} provider_ + * @return {ng.Provider}} + */ function provider(name, provider_) { if (isFunction(provider_) || isArray(provider_)) { - provider_ = providerInjector.instantiate(provider_); + provider_ = /** @type {ng.Provider}*/( + providerInjector.instantiate(/** @type {ng.Injectable}*/(provider_)) + ); } if (!provider_.$get) { throw Error('Provider ' + name + ' must define $get factory method.'); } - return providerCache[name + providerSuffix] = provider_; + return providerCache[name + providerSuffix] = /** @type {ng.Provider}*/(provider_); } function factory(name, factoryFn) { return provider(name, { $get: factoryFn }); } @@ -466,12 +481,12 @@ function createInjector(modulesToLoad) { } function decorator(serviceName, decorFn) { - var origProvider = providerInjector.get(serviceName + providerSuffix), + var origProvider = /** @type {ng.Provider}*/(providerInjector.get(serviceName + providerSuffix)), orig$get = origProvider.$get; origProvider.$get = function() { var origInstance = instanceInjector.invoke(orig$get, origProvider); - return instanceInjector.invoke(decorFn, null, {$delegate: origInstance}); + return instanceInjector.invoke(decorFn, undefined, {$delegate: origInstance}); }; } @@ -523,6 +538,11 @@ function createInjector(modulesToLoad) { // internal Injector //////////////////////////////////// + /** + * @param cache + * @param factory + * @return {ng.Injector} + */ function createInternalInjector(cache, factory) { function getService(serviceName) { @@ -583,12 +603,15 @@ function createInjector(modulesToLoad) { } function instantiate(Type, locals) { - var Constructor = function() {}, - instance, returnedValue; + /** + * @constructor + */ + var Constructor = function() {}; Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype; - instance = new Constructor(); - returnedValue = invoke(Type, instance, locals); + + var instance = new Constructor(); + var returnedValue = invoke(Type, instance, locals); return isObject(returnedValue) ? returnedValue : instance; } @@ -601,3 +624,44 @@ function createInjector(modulesToLoad) { }; } } + + +/** + * @typedef {{ + * invoke: function(ng.Injectable, !Object=, !Object=):*, + * instantiate: function(ng.Injectable, !Object=):*, + * get: function(!string):*, + * annotate: function(!Function):!Array. + * }} + */ +ng.Injector; + +/** + * @typedef {{ + * $get: function():* + * }} + */ +ng.Provider; + + +/** + * @typedef {!(Function|Array.)} + */ +ng.Injectable; + + +/** + * @typedef {{ + * requires: !Array., + * invokeQueue: !Array.>, + * + * service: function(!string, ng.Injectable):ng.Module, + * factory: function(!string, ng.Injectable):ng.Module, + * value: function(!string, *):ng.Module, + * + * filter: function(!string, ng.Injectable):ng.Module, + * + * init: function(ng.Injectable):ng.Module + * }} + */ +ng.Module; diff --git a/src/jqLite.js b/src/jqLite.js index 1d92f2ab0d55..dc3dfb23c33b 100644 --- a/src/jqLite.js +++ b/src/jqLite.js @@ -59,7 +59,7 @@ * - [val()](http://api.jquery.com/val/) * - [wrap()](http://api.jquery.com/wrap/) * - * ## In addtion to the above, Angular provides additional methods to both jQuery and jQuery lite: + * ## In addition to the above, Angular provides additional methods to both jQuery and jQuery lite: * * - `controller(name)` - retrieves the controller of the current element or its parent. By default * retrieves controller associated with the `ngController` directive. If `name` is provided as @@ -71,17 +71,49 @@ * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top * parent element is reached. * - * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery. + * @param {string|Element} element HTML string or Element to be wrapped into jQuery. * @returns {Object} jQuery object. */ -var jqCache = JQLite.cache = {}, - jqName = JQLite.expando = 'ng-' + new Date().getTime(), - jqId = 1, - addEventListenerFn = (window.document.addEventListener + +///////////////////////////////////////////// + +/** + * @constructor + */ +function JQLite(element) { + if (element instanceof JQLite) { + return element; + } + if (!(this instanceof JQLite)) { + if (isString(element) && element.charAt(0) != '<') { + throw Error('selectors not implemented'); + } + return new JQLite(element); + } + + if (isString(element)) { + var div = document.createElement('div'); + // Read about the NoScope elements here: + // http://msdn.microsoft.com/en-us/library/ms533897(VS.85).aspx + div.innerHTML = '
 
' + element; // IE insanity to make NoScope elements work! + div.removeChild(div.firstChild); // remove the superfluous div + JQLiteAddNodes(this, div.childNodes); + this.remove(); // detach the elements from the temporary DOM div. + } else { + JQLiteAddNodes(this, element); + } +} + +///////////////////////////////////////////// + +var jqCache = JQLite.cache = {}; +var jqName = JQLite.expando = 'ng-' + new Date().getTime(); +var jqId = 1; +var addEventListenerFn = (window.document.addEventListener ? function(element, type, fn) {element.addEventListener(type, fn, false);} - : function(element, type, fn) {element.attachEvent('on' + type, fn);}), - removeEventListenerFn = (window.document.removeEventListener + : function(element, type, fn) {element.attachEvent('on' + type, fn);}); +var removeEventListenerFn = (window.document.removeEventListener ? function(element, type, fn) {element.removeEventListener(type, fn, false); } : function(element, type, fn) {element.detachEvent('on' + type, fn); }); @@ -112,7 +144,12 @@ function camelCase(name) { // ///////////////////////////////////////////// -function JQLitePatchJQueryRemove(name, dispatchThis) { +/** + * @param {!string} name + * @param {!boolean=} fireEventOnMainElement if the $destroy event should be fired on the + * current element of the method call. + */ +function JQLitePatchJQueryRemove(name, fireEventOnMainElement) { var originalJqFn = jQuery.fn[name]; originalJqFn = originalJqFn.$original || originalJqFn; removePatch.$original = originalJqFn; @@ -120,7 +157,7 @@ function JQLitePatchJQueryRemove(name, dispatchThis) { function removePatch() { var list = [this], - fireEvent = dispatchThis, + fireEvent = fireEventOnMainElement, set, setIndex, setLength, element, childIndex, childLength, children, fns, events; @@ -132,7 +169,7 @@ function JQLitePatchJQueryRemove(name, dispatchThis) { if (fireEvent) { element.triggerHandler('$destroy'); } else { - fireEvent = !fireEvent; + fireEvent = !fireEvent; // skip the main element and then fire on children } for(childIndex = 0, childLength = (children = element.children()).length; childIndex < childLength; @@ -145,56 +182,91 @@ function JQLitePatchJQueryRemove(name, dispatchThis) { } } -///////////////////////////////////////////// -function JQLite(element) { - if (element instanceof JQLite) { - return element; - } - if (!(this instanceof JQLite)) { - if (isString(element) && element.charAt(0) != '<') { - throw Error('selectors not implemented'); - } - return new JQLite(element); - } - if (isString(element)) { - var div = document.createElement('div'); - // Read about the NoScope elements here: - // http://msdn.microsoft.com/en-us/library/ms533897(VS.85).aspx - div.innerHTML = '
 
' + element; // IE insanity to make NoScope elements work! - div.removeChild(div.firstChild); // remove the superfluous div - JQLiteAddNodes(this, div.childNodes); - this.remove(); // detach the elements from the temporary DOM div. - } else { - JQLiteAddNodes(this, element); - } +/** + * @param {!Node} node + * @return {!Node} + */ +function JQLiteClone(node) { + return /** @type {!Node} */(node.cloneNode(true)); } -function JQLiteClone(element) { - return element.cloneNode(true); -} -function JQLiteDealoc(element){ - JQLiteRemoveData(element); - for ( var i = 0, children = element.childNodes || []; i < children.length; i++) { +/** + * @param {!Node} node + */ +function JQLiteDealoc(node){ + JQLiteRemoveData(node); + for ( var i = 0, children = node.childNodes || []; i < children.length; i++) { JQLiteDealoc(children[i]); } } -function JQLiteUnbind(element, type, fn) { - var events = JQLiteExpandoStore(element, 'events'), - handle = JQLiteExpandoStore(element, 'handle'); + +/** + * @param {!Node} node + * @param {!string} type + * @param {!Function} fn + */ +function JQLiteBind(node, type, fn){ + var events = JQLiteExpandoStore(node, 'events'), + handle = JQLiteExpandoStore(node, 'handle'); + + if (!events) JQLiteExpandoStore(node, 'events', events = {}); + if (!handle) JQLiteExpandoStore(node, 'handle', handle = createEventHandler(node, events)); + + forEach(type.split(' '), function(type){ + var eventFns = events[type]; + + if (!eventFns) { + if (type == 'mouseenter' || type == 'mouseleave') { + var counter = 0; + + events.mouseenter = []; + events.mouseleave = []; + + JQLiteBind(node, 'mouseover', function(event) { + counter++; + if (counter == 1) { + handle(event, 'mouseenter'); + } + }); + JQLiteBind(node, 'mouseout', function(event) { + counter --; + if (counter == 0) { + handle(event, 'mouseleave'); + } + }); + } else { + addEventListenerFn(node, type, handle); + events[type] = []; + } + eventFns = events[type] + } + eventFns.push(fn); + }); +} + + +/** + * @param {!Node} node + * @param {!string=} type + * @param {!Function=} fn + */ +function JQLiteUnbind(node, type, fn) { + var events = JQLiteExpandoStore(node, 'events'), + handle = JQLiteExpandoStore(node, 'handle'); if (!handle) return; //no listeners registered if (isUndefined(type)) { forEach(events, function(eventHandler, type) { - removeEventListenerFn(element, type, eventHandler); + removeEventListenerFn(node, type, eventHandler); delete events[type]; }); } else { if (isUndefined(fn)) { - removeEventListenerFn(element, type, events[type]); + removeEventListenerFn(node, type, events[type]); delete events[type]; } else { arrayRemove(events[type], fn); @@ -202,27 +274,160 @@ function JQLiteUnbind(element, type, fn) { } } -function JQLiteRemoveData(element) { - var expandoId = element[jqName], + +/** + * @param {!Element} element + * @param {!string} eventName + */ +function JQLiteTriggerHandler(element, eventName) { + var eventFns = (JQLiteExpandoStore(element, 'events') || {})[eventName]; + + forEach(eventFns, function(fn) { + fn.call(element, null); + }); +} + + +/** + * @param {!Node} origNode + * @param {!Node} replaceNode + */ +function JQLiteReplaceWith(origNode, replaceNode) { + var index, parent = origNode.parentNode; + JQLiteDealoc(origNode); + forEach(new JQLite(replaceNode), function(node){ + if (index) { + parent.insertBefore(node, index.nextSibling); + } else { + parent.replaceChild(node, origNode); + } + index = node; + }); +} + + +/** + * @param {!Element} element + * @param {!Node} node + */ +function JQLiteAppend(element, node) { + forEach(new JQLite(node), function(child){ + if (element.nodeType === 1 || element.nodeType === 11) { + element.appendChild(child); + } + }); +} + + +/** + * @param {!Element} element + * @param {!Node} node + */ +function JQLitePrepend(element, node) { + if (element.nodeType === 1) { + var index = element.firstChild; + forEach(new JQLite(node), function(child){ + if (index) { + element.insertBefore(child, index); + } else { + element.appendChild(child); + index = child; + } + }); + } +} + + +/** + * @param {!Element} element + * @param {!Element} wrapElement + */ +function JQLiteWrap(element, wrapElement) { + wrapElement = jqLite(wrapElement)[0]; + var parent = element.parentNode; + if (parent) { + parent.replaceChild(wrapElement, element); + } + wrapElement.appendChild(element); +} + + +/** + * @param {!Node} node + */ +function JQLiteRemove(node) { + JQLiteDealoc(node); + var parent = node.parentNode; + if (parent) parent.removeChild(node); +} + + +/** + * @param {!Node} origNode + * @param {!Node} newNode + */ +function JQLiteAfter(origNode, newNode) { + var index = origNode, parent = origNode.parentNode; + forEach(new JQLite(newNode), function(node){ + parent.insertBefore(node, index.nextSibling); + index = node; + }); +} + + +/** + * @param {!Element} element + * @return {!Array.} + */ +function JQLiteChildren(element) { + var children = []; + forEach(element.childNodes, function(element){ + if (element.nodeType === 1) + children.push(element); + }); + return children; +} + + +/** + * @param {!Element} element + * @return {!NodeList|!Array} + */ +function JQLiteContents(element) { + return element.childNodes || []; +} + + +/** + * @param {!Node} node + */ +function JQLiteRemoveData(node) { + var expandoId = node[jqName], expandoStore = jqCache[expandoId]; if (expandoStore) { if (expandoStore.handle) { expandoStore.events.$destroy && expandoStore.handle({}, '$destroy'); - JQLiteUnbind(element); + JQLiteUnbind(node); } delete jqCache[expandoId]; - element[jqName] = undefined; // ie does not allow deletion of attributes on elements. + node[jqName] = undefined; // ie does not allow deletion of attributes on elements. } } -function JQLiteExpandoStore(element, key, value) { - var expandoId = element[jqName], + +/** + * @param {!Node} node + * @param {!string} key + * @param {*=} value + */ +function JQLiteExpandoStore(node, key, value) { + var expandoId = node[jqName], expandoStore = jqCache[expandoId || -1]; if (isDefined(value)) { if (!expandoStore) { - element[jqName] = expandoId = jqNextId(); + node[jqName] = expandoId = jqNextId(); expandoStore = jqCache[expandoId] = {}; } expandoStore[key] = value; @@ -231,14 +436,21 @@ function JQLiteExpandoStore(element, key, value) { } } -function JQLiteData(element, key, value) { - var data = JQLiteExpandoStore(element, 'data'), + +/** + * @param {!Node} node + * @param {!(string|Object)} key + * @param {*=} value + * @return {*} + */ +function JQLiteData(node, key, value) { + var data = JQLiteExpandoStore(node, 'data'), isSetter = isDefined(value), keyDefined = !isSetter && isDefined(key), isSimpleGetter = keyDefined && !isObject(key); if (!data && !isSimpleGetter) { - JQLiteExpandoStore(element, 'data', data = {}); + JQLiteExpandoStore(node, 'data', data = {}); } if (isSetter) { @@ -249,7 +461,7 @@ function JQLiteData(element, key, value) { // don't create data in this case. return data && data[key]; } else { - extend(data, key); + extend(data, /** @type {Object} */(key)); } } else { return data; @@ -257,11 +469,171 @@ function JQLiteData(element, key, value) { } } -function JQLiteHasClass(element, selector) { + +/** + * @param {!Element} element + * @param {!string} name + */ +function JQLiteRemoveAttr(element,name) { + element.removeAttribute(name); +} + +/** + * @param {!Element} element + * @param {!string} className + * @return {!boolean} + */ +function JQLiteHasClass(element, className) { return ((" " + element.className + " ").replace(/[\n\t]/g, " "). - indexOf( " " + selector + " " ) > -1); + indexOf( " " + className + " " ) > -1); } + +/** + * @param {!Element} element + * @param {!string} name + * @param {!string=} value + * @return {string|undefined} + */ +function JQLiteCss(element, name, value) { + name = camelCase(name); + + if (isDefined(value)) { + element.style[name] = value; + } else { + var val; + + if (msie <= 8) { + // this is some IE specific weirdness that jQuery 1.6.4 does not sure why + val = element.currentStyle && element.currentStyle[name]; + if (val === '') val = 'auto'; + } + + val = val || element.style[name]; + + if (msie <= 8) { + // jquery weirdness :-/ + val = (val === '') ? undefined : val; + } + + return val; + } +} + + +/** + * @param {!Element} element + * @param {!string} name + * @param {!(string|boolean)} value + * @return {(string|undefined)} + */ +function JQLiteAttr(element, name, value){ + var lowercasedName = lowercase(name); + if (BOOLEAN_ATTR[lowercasedName]) { + if (isDefined(value)) { + if (!!value) { + element[name] = true; + element.setAttribute(name, lowercasedName); + } else { + element[name] = false; + element.removeAttribute(lowercasedName); + } + } else { + return (element[name] || + (element.attributes.getNamedItem(name)|| noop).specified) + ? lowercasedName + : undefined; + } + } else if (isDefined(value)) { + element.setAttribute(name, value); + } else if (element.getAttribute) { + // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code + // some elements (e.g. Document) don't have get attribute, so return undefined + var ret = element.getAttribute(name, 2); + // normalize non-existing attributes to undefined (as jQuery) + return ret === null ? undefined : ret; + } +} + + +/** + * @param {!Node} element + * @param {!string} name + * @param {*=} value + * @return {*} + */ +function JQLiteProp(element, name, value) { + if (isDefined(value)) { + element[name] = value; + } else { + return element[name]; + } +} + + +/** + * @param {!Node} node + * @param {!string=} value + * @return {string|undefined} + */ +function JQLiteTextOldIE(node, value) { + if (node.nodeType == 1 /** Element */) { + if (isUndefined(value)) + return node.innerText; + node.innerText = value; + } else { + if (isUndefined(value)) return node.nodeValue; + node.nodeValue = /** @type {string} */(value); + } +} + + +/** + * @param {!Element} element + * @param {!string=} value + * @return {string|undefined} + */ +function JQLiteText(element, value) { + if (isUndefined(value)) { + return element.textContent; + } + element.textContent = value; +} + + +/** + * @param {!Node} element + * @param {!string=} value + * @return {*} + */ +function JQLiteVal(element, value) { + if (isUndefined(value)) { + return element.value; + } + element.value = value; +} + + +/** + * @param {!Element} element + * @param {!string=} value + * @return {string|undefined} + */ +function JQLiteHtml(element, value) { + if (isUndefined(value)) { + return element.innerHTML; + } + for (var i = 0, childNodes = element.childNodes; i < childNodes.length; i++) { + JQLiteDealoc(childNodes[i]); + } + element.innerHTML = value; +} + + +/** + * @param {!Element} element + * @param {!string} cssClasses + */ function JQLiteRemoveClass(element, cssClasses) { if (cssClasses) { forEach(cssClasses.split(' '), function(cssClass) { @@ -274,6 +646,11 @@ function JQLiteRemoveClass(element, cssClasses) { } } + +/** + * @param {!Element} element + * @param {!string} cssClasses + */ function JQLiteAddClass(element, cssClasses) { if (cssClasses) { forEach(cssClasses.split(' '), function(cssClass) { @@ -284,6 +661,62 @@ function JQLiteAddClass(element, cssClasses) { } } + +/** + * @param {!Element} element + * @param {!string} className + * @param {!boolean=} condition + */ +function JQLiteToggleClass(element, className, condition) { + if (isUndefined(condition)) { + condition = !JQLiteHasClass(element, className); + } + (condition ? JQLiteAddClass : JQLiteRemoveClass)(element, className); +} + + +/** + * @param {!Node} node + * @return {Element} + */ +function JQLiteParent(node) { + var parent = node.parentNode; + return /** @type {Element} */(parent && parent.nodeType !== 11 ? parent : null); +} + + +/** + * @param {!Element} element + * @return {Element} + */ +function JQLiteNext(element) { + if (element.nextElementSibling) { + return element.nextElementSibling; + } + + // IE8 doesn't have nextElementSibling + var elm = element.nextSibling; + while (elm != null && elm.nodeType !== 1) { + elm = elm.nextSibling; + } + return /** @type {Element} */(elm); +} + + +/** + * @param {!Element} element + * @param {!string} selector + * @return {!NodeList} + */ +function JQLiteFind(element, selector) { + return element.getElementsByTagName(selector); +} + + +/** + * @param {!JQLite} root + * @param {!(Element|NodeList|JQLite|Array)} elements + */ function JQLiteAddNodes(root, elements) { if (elements) { elements = (!elements.nodeName && isDefined(elements.length) && !isWindow(elements)) @@ -295,29 +728,59 @@ function JQLiteAddNodes(root, elements) { } } +/** + * @param {!Element} element + * @param {!string} name + * @return {!Object|undefined} + */ function JQLiteController(element, name) { return JQLiteInheritedData(element, '$' + (name || 'ngController' ) + 'Controller'); } -function JQLiteInheritedData(element, name, value) { - element = jqLite(element); - // if element is the document object work with the html element instead +/** + * @param {!Element} element + * @return {!ng.Injector} + */ +function JQLiteInjector(element) { + return JQLiteInheritedData(element, '$injector'); +} + + +/** + * @param {!Node} node + * @param {string} name + * @param {*=} value Bogus param to make this fn look like getter/setter (fn.length === 3) + */ +function JQLiteInheritedData(node, name, value) { + var $element = jqLite(node); + + // if node is the document object work with the html node instead // this makes $(document).scope() possible - if(element[0].nodeType == 9) { - element = element.find('html'); + if(node.nodeType == 9) { + $element = $element.find('html'); } - while (element.length) { - if (value = element.data(name)) return value; - element = element.parent(); + while ($element.length) { + if (value = $element.data(name)) return value; + $element = $element.parent(); } } + +/** + * @param {!Element} element + * @return {!ng.Scope} + */ +function JQLiteScope(element) { + return JQLiteInheritedData(element, '$scope'); +} + ////////////////////////////////////////// // Functions which are declared directly. ////////////////////////////////////////// -var JQLitePrototype = JQLite.prototype = { +var JQLitePrototype = extend(JQLite.prototype, /** @lends {JQLite.prototype} */{ + ready: function(fn) { var fired = false; @@ -329,7 +792,7 @@ var JQLitePrototype = JQLite.prototype = { this.bind('DOMContentLoaded', trigger); // works for modern browsers and IE9 // we can not use jqLite since we are not done loading and jQuery could be loaded later. - JQLite(window).bind('load', trigger); // fallback to window.onload for others + new JQLite(window).bind('load', trigger); // fallback to window.onload for others }, toString: function() { var value = []; @@ -345,7 +808,7 @@ var JQLitePrototype = JQLite.prototype = { push: push, sort: [].sort, splice: [].splice -}; +}); ////////////////////////////////////////// // Functions iterating getter/setters. @@ -369,122 +832,36 @@ function getBooleanAttrName(element, name) { return booleanAttr && BOOLEAN_ELEMENTS[element.nodeName] && booleanAttr; } -forEach({ - data: JQLiteData, - inheritedData: JQLiteInheritedData, - scope: function(element) { - return JQLiteInheritedData(element, '$scope'); - }, +forEach(/** @lends {JQLite.prototype} */{ - controller: JQLiteController , - injector: function(element) { - return JQLiteInheritedData(element, '$injector'); - }, + data: /** @type function(!string, *=):* */(JQLiteData), - removeAttr: function(element,name) { - element.removeAttribute(name); - }, + inheritedData: /** @type function(!string):* */(JQLiteInheritedData), - hasClass: JQLiteHasClass, + scope: /** @type function():!ng.Scope */(JQLiteScope), - css: function(element, name, value) { - name = camelCase(name); + controller: /** @type function():!Object */(JQLiteController), - if (isDefined(value)) { - element.style[name] = value; - } else { - var val; + injector: /** @type function():!ng.Injector */(JQLiteInjector), - if (msie <= 8) { - // this is some IE specific weirdness that jQuery 1.6.4 does not sure why - val = element.currentStyle && element.currentStyle[name]; - if (val === '') val = 'auto'; - } + removeAttr: /** @type function(!string):undefined */(JQLiteRemoveAttr), - val = val || element.style[name]; + hasClass: /** @type function(!string):!boolean */(JQLiteHasClass), - if (msie <= 8) { - // jquery weirdness :-/ - val = (val === '') ? undefined : val; - } + css: /** @type function(!string, !string):(string|undefined) */(JQLiteCss), - return val; - } - }, + attr: /** @type function(!string, !(string|boolean)=):(string|undefined) */(JQLiteAttr), - attr: function(element, name, value){ - var lowercasedName = lowercase(name); - if (BOOLEAN_ATTR[lowercasedName]) { - if (isDefined(value)) { - if (!!value) { - element[name] = true; - element.setAttribute(name, lowercasedName); - } else { - element[name] = false; - element.removeAttribute(lowercasedName); - } - } else { - return (element[name] || - (element.attributes.getNamedItem(name)|| noop).specified) - ? lowercasedName - : undefined; - } - } else if (isDefined(value)) { - element.setAttribute(name, value); - } else if (element.getAttribute) { - // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code - // some elements (e.g. Document) don't have get attribute, so return undefined - var ret = element.getAttribute(name, 2); - // normalize non-existing attributes to undefined (as jQuery) - return ret === null ? undefined : ret; - } - }, + prop: /** @type function(!string, *=):* */(JQLiteProp), - prop: function(element, name, value) { - if (isDefined(value)) { - element[name] = value; - } else { - return element[name]; - } - }, + text: /** @type function(!string=):!* */(extend((msie < 9) ? JQLiteTextOldIE : JQLiteText, {$dv:''})), - text: extend((msie < 9) - ? function(element, value) { - if (element.nodeType == 1 /** Element */) { - if (isUndefined(value)) - return element.innerText; - element.innerText = value; - } else { - if (isUndefined(value)) - return element.nodeValue; - element.nodeValue = value; - } - } - : function(element, value) { - if (isUndefined(value)) { - return element.textContent; - } - element.textContent = value; - }, {$dv:''}), + val: /** @type function(!string=):(string|undefined) */(JQLiteVal), - val: function(element, value) { - if (isUndefined(value)) { - return element.value; - } - element.value = value; - }, + html: /** @type function(!string=):(string|undefined) */(JQLiteHtml) - html: function(element, value) { - if (isUndefined(value)) { - return element.innerHTML; - } - for (var i = 0, childNodes = element.childNodes; i < childNodes.length; i++) { - JQLiteDealoc(childNodes[i]); - } - element.innerHTML = value; - } }, function(fn, name){ /** * Properties: writes return selection, reads return first value @@ -566,9 +943,13 @@ function createEventHandler(element, events) { // as they would cause memory leaks in IE8. if (msie <= 8) { // IE7/8 does not allow to delete property on native object + /** + * @type {*} + */ + var releaseMem = null; event.preventDefault = null; event.stopPropagation = null; - event.isDefaultPrevented = null; + event.isDefaultPrevented = /** @type {function (): ?} */(releaseMem); } else { // It shouldn't affect normal browsers (native methods are defined on prototype). delete event.preventDefault; @@ -585,164 +966,48 @@ function createEventHandler(element, events) { // These functions chain results into a single // selector. ////////////////////////////////////////// -forEach({ - removeData: JQLiteRemoveData, +forEach(/** @lends {JQLite.prototype} */{ - dealoc: JQLiteDealoc, + removeData: /** @type {function():!JQLite} */(JQLiteRemoveData), - bind: function bindFn(element, type, fn){ - var events = JQLiteExpandoStore(element, 'events'), - handle = JQLiteExpandoStore(element, 'handle'); + dealoc: /** @type {function():!JQLite}*/(JQLiteDealoc), - if (!events) JQLiteExpandoStore(element, 'events', events = {}); - if (!handle) JQLiteExpandoStore(element, 'handle', handle = createEventHandler(element, events)); + bind: /** @type {function(!string, !Function):!JQLite} */(JQLiteBind), - forEach(type.split(' '), function(type){ - var eventFns = events[type]; + unbind: /** @type {function(string=, Function=):!JQLite}*/(JQLiteUnbind), - if (!eventFns) { - if (type == 'mouseenter' || type == 'mouseleave') { - var counter = 0; + replaceWith: /** @type {function(!Node):!JQLite} */(JQLiteReplaceWith), - events.mouseenter = []; - events.mouseleave = []; + children: /** @type {function():!JQLite} */(JQLiteChildren), - bindFn(element, 'mouseover', function(event) { - counter++; - if (counter == 1) { - handle(event, 'mouseenter'); - } - }); - bindFn(element, 'mouseout', function(event) { - counter --; - if (counter == 0) { - handle(event, 'mouseleave'); - } - }); - } else { - addEventListenerFn(element, type, handle); - events[type] = []; - } - eventFns = events[type] - } - eventFns.push(fn); - }); - }, + contents: /** @type {function():!JQLite} */(JQLiteContents), - unbind: JQLiteUnbind, + append: /** @type {function(!Node):!JQLite} */(JQLiteAppend), - replaceWith: function(element, replaceNode) { - var index, parent = element.parentNode; - JQLiteDealoc(element); - forEach(new JQLite(replaceNode), function(node){ - if (index) { - parent.insertBefore(node, index.nextSibling); - } else { - parent.replaceChild(node, element); - } - index = node; - }); - }, + prepend: /** @type {function(!Node):!JQLite} */(JQLitePrepend), - children: function(element) { - var children = []; - forEach(element.childNodes, function(element){ - if (element.nodeType === 1) - children.push(element); - }); - return children; - }, + wrap: /** @type {function(!Element):!JQLite} */(JQLiteWrap), - contents: function(element) { - return element.childNodes || []; - }, + remove: /** @type {function():!JQLite} */(JQLiteRemove), - append: function(element, node) { - forEach(new JQLite(node), function(child){ - if (element.nodeType === 1 || element.nodeType === 11) { - element.appendChild(child); - } - }); - }, + after: /** @type {function(!Node):!JQLite} */(JQLiteAfter), - prepend: function(element, node) { - if (element.nodeType === 1) { - var index = element.firstChild; - forEach(new JQLite(node), function(child){ - if (index) { - element.insertBefore(child, index); - } else { - element.appendChild(child); - index = child; - } - }); - } - }, - - wrap: function(element, wrapNode) { - wrapNode = jqLite(wrapNode)[0]; - var parent = element.parentNode; - if (parent) { - parent.replaceChild(wrapNode, element); - } - wrapNode.appendChild(element); - }, - - remove: function(element) { - JQLiteDealoc(element); - var parent = element.parentNode; - if (parent) parent.removeChild(element); - }, - - after: function(element, newElement) { - var index = element, parent = element.parentNode; - forEach(new JQLite(newElement), function(node){ - parent.insertBefore(node, index.nextSibling); - index = node; - }); - }, + addClass: /** @type {function(!string):!JQLite}*/(JQLiteAddClass), - addClass: JQLiteAddClass, - removeClass: JQLiteRemoveClass, + removeClass: /** @type {function(!string):!JQLite}*/(JQLiteRemoveClass), - toggleClass: function(element, selector, condition) { - if (isUndefined(condition)) { - condition = !JQLiteHasClass(element, selector); - } - (condition ? JQLiteAddClass : JQLiteRemoveClass)(element, selector); - }, + toggleClass: /** @type {function(!string, !boolean=):!JQLite} */(JQLiteToggleClass), - parent: function(element) { - var parent = element.parentNode; - return parent && parent.nodeType !== 11 ? parent : null; - }, - - next: function(element) { - if (element.nextElementSibling) { - return element.nextElementSibling; - } + parent: /** @type {function():!JQLite} */(JQLiteParent), - // IE8 doesn't have nextElementSibling - var elm = element.nextSibling; - while (elm != null && elm.nodeType !== 1) { - elm = elm.nextSibling; - } - return elm; - }, + next: /** @type {function():!JQLite} */(JQLiteNext), - find: function(element, selector) { - return element.getElementsByTagName(selector); - }, + find: /** @type {function(!string):!JQLite} */(JQLiteFind), - clone: JQLiteClone, + clone: /** @type {function():!JQLite}*/(JQLiteClone), - triggerHandler: function(element, eventName) { - var eventFns = (JQLiteExpandoStore(element, 'events') || {})[eventName]; + triggerHandler: /** @type {function(!string):!JQLite} */(JQLiteTriggerHandler) - forEach(eventFns, function(fn) { - fn.call(element, null); - }); - } }, function(fn, name){ /** * chaining functions diff --git a/src/loader.js b/src/loader.js index ecb166085460..b769d26d9034 100644 --- a/src/loader.js +++ b/src/loader.js @@ -15,7 +15,7 @@ function setupModuleLoader(window) { } return ensure(ensure(window, 'angular', Object), 'module', function() { - /** @type {Object.} */ + /** @type {Object.} */ // TODO(i): do we want to allow null?!? var modules = {}; /** @@ -60,9 +60,9 @@ function setupModuleLoader(window) { * @param {!string} name The name of the module to create or retrieve. * @param {Array.=} requires If specified then new module is being created. If unspecified then the * the module is being retrieved for further configuration. - * @param {Function} configFn Optional configuration function for the module. Same as + * @param {Function=} configFn Optional configuration function for the module. Same as * {@link angular.Module#config Module#config()}. - * @returns {module} new module with the {@link angular.Module} api. + * @returns {ng.Module} new module with the {@link angular.Module} api. */ return function module(name, requires, configFn) { if (requires && modules.hasOwnProperty(name)) { @@ -81,8 +81,7 @@ function setupModuleLoader(window) { var config = invokeLater('$injector', 'invoke'); - /** @type {angular.Module} */ - var moduleInstance = { + var moduleInstance = /** @lends {ng.Module}*/{ // Private state _invokeQueue: invokeQueue, _runBlocks: runBlocks, @@ -91,9 +90,11 @@ function setupModuleLoader(window) { * @ngdoc property * @name angular.Module#requires * @propertyOf angular.Module - * @returns {Array.} List of module names which must be loaded before this module. + * * @description * Holds the list of modules which the injector will load before the current module is loaded. + * + * @type {Array.} List of module names which must be loaded before this module. */ requires: requires, @@ -101,8 +102,7 @@ function setupModuleLoader(window) { * @ngdoc property * @name angular.Module#name * @propertyOf angular.Module - * @returns {string} Name of the module. - * @description + * @type {string} Name of the module. */ name: name, @@ -111,10 +111,12 @@ function setupModuleLoader(window) { * @ngdoc method * @name angular.Module#provider * @methodOf angular.Module - * @param {string} name service name - * @param {Function} providerType Construction function for creating new instance of the service. + * * @description * See {@link AUTO.$provide#provider $provide.provider()}. + * + * @param {string} name service name + * @param {Function} providerType Construction function for creating new instance of the service. */ provider: invokeLater('$provide', 'provider'), @@ -122,10 +124,12 @@ function setupModuleLoader(window) { * @ngdoc method * @name angular.Module#factory * @methodOf angular.Module - * @param {string} name service name - * @param {Function} providerFunction Function for creating new instance of the service. + * * @description * See {@link AUTO.$provide#factory $provide.factory()}. + * + * @param {string} name service name + * @param {Function} providerFunction Function for creating new instance of the service. */ factory: invokeLater('$provide', 'factory'), @@ -133,10 +137,12 @@ function setupModuleLoader(window) { * @ngdoc method * @name angular.Module#service * @methodOf angular.Module - * @param {string} name service name - * @param {Function} constructor A constructor function that will be instantiated. + * * @description * See {@link AUTO.$provide#service $provide.service()}. + * + * @param {string} name service name + * @param {Function} constructor A constructor function that will be instantiated. */ service: invokeLater('$provide', 'service'), @@ -144,10 +150,12 @@ function setupModuleLoader(window) { * @ngdoc method * @name angular.Module#value * @methodOf angular.Module - * @param {string} name service name - * @param {*} object Service instance object. + * * @description * See {@link AUTO.$provide#value $provide.value()}. + * + * @param {string} name service name + * @param {*} object Service instance object. */ value: invokeLater('$provide', 'value'), @@ -155,11 +163,13 @@ function setupModuleLoader(window) { * @ngdoc method * @name angular.Module#constant * @methodOf angular.Module - * @param {string} name constant name - * @param {*} object Constant value. + * * @description * Because the constant are fixed, they get applied before other provide methods. * See {@link AUTO.$provide#constant $provide.constant()}. + * + * @param {string} name constant name + * @param {*} object Constant value. */ constant: invokeLater('$provide', 'constant', 'unshift'), @@ -167,10 +177,12 @@ function setupModuleLoader(window) { * @ngdoc method * @name angular.Module#filter * @methodOf angular.Module - * @param {string} name Filter name. - * @param {Function} filterFactory Factory function for creating new instance of filter. + * * @description * See {@link ng.$filterProvider#register $filterProvider.register()}. + * + * @param {string} name Filter name. + * @param {Function} filterFactory Factory function for creating new instance of filter. */ filter: invokeLater('$filterProvider', 'register'), @@ -178,10 +190,12 @@ function setupModuleLoader(window) { * @ngdoc method * @name angular.Module#controller * @methodOf angular.Module - * @param {string} name Controller name. - * @param {Function} constructor Controller constructor function. + * * @description * See {@link ng.$controllerProvider#register $controllerProvider.register()}. + * + * @param {string} name Controller name. + * @param {Function} constructor Controller constructor function. */ controller: invokeLater('$controllerProvider', 'register'), @@ -189,11 +203,13 @@ function setupModuleLoader(window) { * @ngdoc method * @name angular.Module#directive * @methodOf angular.Module + * + * @description + * See {@link ng.$compileProvider#directive $compileProvider.directive()}. + * * @param {string} name directive name * @param {Function} directiveFactory Factory function for creating new instance of * directives. - * @description - * See {@link ng.$compileProvider#directive $compileProvider.directive()}. */ directive: invokeLater('$compileProvider', 'directive'), @@ -201,10 +217,12 @@ function setupModuleLoader(window) { * @ngdoc method * @name angular.Module#config * @methodOf angular.Module - * @param {Function} configFn Execute this function on module load. Useful for service - * configuration. + * * @description * Use this method to register work which needs to be performed on module loading. + * + * @param {Function} configFn Execute this function on module load. Useful for service + * configuration. */ config: config, @@ -212,14 +230,16 @@ function setupModuleLoader(window) { * @ngdoc method * @name angular.Module#run * @methodOf angular.Module - * @param {Function} initializationFn Execute this function after injector creation. - * Useful for application initialization. + * * @description * Use this method to register work which should be performed when the injector is done * loading all modules. + * + * @param {Function} initializationFn Execute this function after injector creation. + * Useful for application initialization. */ - run: function(block) { - runBlocks.push(block); + run: function(initializationFn) { + runBlocks.push(initializationFn); return this; } }; @@ -233,17 +253,17 @@ function setupModuleLoader(window) { /** * @param {string} provider * @param {string} method - * @param {String=} insertMethod - * @returns {angular.Module} + * @param {string=} insertMethod + * @returns {function(...[*]):ng.Module} */ function invokeLater(provider, method, insertMethod) { return function() { - invokeQueue[insertMethod || 'push']([provider, method, arguments]); + /** @type {Function} */( + /** @type {Object} */(invokeQueue)[insertMethod || 'push'])([provider, method, arguments]); return moduleInstance; } } }); }; }); - } diff --git a/src/ng/anchorScroll.js b/src/ng/anchorScroll.js index 0ddc9c78af65..d19ddefd9ef9 100644 --- a/src/ng/anchorScroll.js +++ b/src/ng/anchorScroll.js @@ -12,6 +12,8 @@ * * It also watches the `$location.hash()` and scroll whenever it changes to match any anchor. * This can be disabled by calling `$anchorScrollProvider.disableAutoScrolling()`. + * + * @constructor */ function $AnchorScrollProvider() { diff --git a/src/ng/browser.js b/src/ng/browser.js index fa050d54511f..4a6878ca4094 100644 --- a/src/ng/browser.js +++ b/src/ng/browser.js @@ -15,12 +15,13 @@ * service, which can be used for convenient testing of the application without the interaction with * the real browser apis. */ + /** - * @param {object} window The global window object. - * @param {object} document jQuery wrapped document. - * @param {function()} XHR XMLHttpRequest constructor. - * @param {object} $log console.log or an object with the same interface. - * @param {object} $sniffer $sniffer service + * @param {Object} window The global window object. + * @param {Object} document jQuery wrapped document. + * @param {Object} $log console.log or an object with the same interface. + * @param {Object} $sniffer $sniffer service + * @constructor */ function Browser(window, document, $log, $sniffer) { var self = this, @@ -105,12 +106,11 @@ function Browser(window, document, $log, $sniffer) { }; /** - * @param {number} interval How often should browser call poll functions (ms) - * @param {function()} setTimeout Reference to a real or fake `setTimeout` function. - * - * @description * Configures the poller to run in the specified intervals, using the specified * setTimeout fn and kicks it off. + * + * @param {number} interval How often should browser call poll functions (ms) + * @param {function(function(), number=)} setTimeout Reference to a real or fake `setTimeout` fn. */ function startPoller(interval, setTimeout) { (function check() { @@ -143,7 +143,7 @@ function Browser(window, document, $log, $sniffer) { * NOTE: this api is intended for use only by the $location service. Please use the * {@link ng.$location $location service} to change url. * - * @param {string} url New url (when used as setter) + * @param {string=} url New url (when used as setter) * @param {boolean=} replace Should new url replace current history record ? */ self.url = function(url, replace) { @@ -205,7 +205,7 @@ function Browser(window, document, $log, $sniffer) { * @param {function(string)} listener Listener function to be called when url changes. * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous. */ - self.onUrlChange = function(callback) { + self.onUrlChange = function(listener) { if (!urlChangeInit) { // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera) // don't fire popstate when user change the address bar and don't fire hashchange when url @@ -221,8 +221,8 @@ function Browser(window, document, $log, $sniffer) { urlChangeInit = true; } - urlChangeListeners.push(callback); - return callback; + urlChangeListeners.push(listener); + return listener; }; ////////////////////////////////////////////////////////////// @@ -230,10 +230,7 @@ function Browser(window, document, $log, $sniffer) { ////////////////////////////////////////////////////////////// /** - * Returns current - * (always relative - without domain) - * - * @returns {string=} + * @returns {string|undefined} Returns current (always relative - without domain) */ self.baseHref = function() { var href = baseElement.attr('href'); @@ -251,9 +248,6 @@ function Browser(window, document, $log, $sniffer) { * @name ng.$browser#cookies * @methodOf ng.$browser * - * @param {string=} name Cookie name - * @param {string=} value Cokkie value - * * @description * The cookies method provides a 'private' low level access to browser cookies. * It is not meant to be used directly, use the $cookie service instead. @@ -265,7 +259,9 @@ function Browser(window, document, $log, $sniffer) { *
  • cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that way)
  • * * - * @returns {Object} Hash of all cookies (if called without any parameter) + * @param {string=} name Cookie name + * @param {(string|undefined)=} value Cookie value + * @returns {Object|undefined} Hash of all cookies (if called without any parameter) */ self.cookies = function(name, value) { var cookieLength, cookieArray, cookie, i, index; @@ -355,6 +351,10 @@ function Browser(window, document, $log, $sniffer) { } + +/** + * @constructor + */ function $BrowserProvider(){ this.$get = ['$window', '$log', '$sniffer', '$document', function( $window, $log, $sniffer, $document){ diff --git a/src/ng/cacheFactory.js b/src/ng/cacheFactory.js index ce690ebfe2a4..08a54c50e2b9 100644 --- a/src/ng/cacheFactory.js +++ b/src/ng/cacheFactory.js @@ -5,15 +5,14 @@ * @description * Factory that constructs cache objects. * - * * @param {string} cacheId Name or id of the newly created cache. * @param {object=} options Options object that specifies the cache behavior. Properties: * * - `{number=}` `capacity` — turns the cache into LRU cache. * - * @returns {object} Newly created cache object with the following set of methods: + * @returns {Object} Newly created cache object with the following set of methods: * - * - `{object}` `info()` — Returns id, size, and options of cache. + * - `{Object}` `info()` — Returns id, size, and options of cache. * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns it. * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss. * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache. @@ -21,6 +20,10 @@ * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory. * */ + +/** + * @constructor + */ function $CacheFactoryProvider() { this.$get = function() { @@ -165,6 +168,10 @@ function $CacheFactoryProvider() { * See {@link ng.$cacheFactory $cacheFactory}. * */ + +/** + * @constructor + */ function $TemplateCacheProvider() { this.$get = ['$cacheFactory', function($cacheFactory) { return $cacheFactory('templates'); diff --git a/src/ng/compile.js b/src/ng/compile.js index 18adc2c9eff7..3c787b2b2d39 100644 --- a/src/ng/compile.js +++ b/src/ng/compile.js @@ -92,11 +92,11 @@ var NON_ASSIGNABLE_MODEL_EXPRESSION = 'Non-assignable model expression: '; * * - * @param {string|DOMElement} element Element or HTML string to compile into a template function. - * @param {function(angular.Scope[, cloneAttachFn]} transclude function available to directives. + * @param {string|Element} element Element or HTML string to compile into a template function. + * @param {function(ng.Scope, ng.$compile.cloneAttachFn=)} transclude function available to directives. * @param {number} maxPriority only apply directives lower then given priority (Only effects the * root element(s), not their children) - * @returns {function(scope[, cloneAttachFn])} a link function which is used to bind template + * @returns {function(ng.Scope, ng.$compile.cloneAttachFn=)} a link function which is used to bind template * (a DOM element/tree) to a scope. Where: * * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to. @@ -145,11 +145,9 @@ var NON_ASSIGNABLE_MODEL_EXPRESSION = 'Non-assignable model expression: '; /** * @ngdoc service * @name ng.$compileProvider - * @function * - * @description + * @constructor */ -$CompileProvider.$inject = ['$provide']; function $CompileProvider($provide) { var hasDirectives = {}, Suffix = 'Directive', @@ -167,11 +165,11 @@ function $CompileProvider($provide) { * @description * Register a new directives with the compiler. * - * @param {string} name Name of the directive in camel-case. (ie ngBind which will match as + * @param {!(string|Object)} name Name of the directive in camel-case. (ie ngBind which will match as * ng-bind). - * @param {function} directiveFactory An injectable directive factroy function. See {@link guide/directive} for more + * @param {!Function} directiveFactory An injectable directive factory function. See {@link guide/directive} for more * info. - * @returns {ng.$compileProvider} Self for chaining. + * @returns {$CompileProvider} Returns self for method chaining. */ this.directive = function registerDirective(name, directiveFactory) { if (isString(name)) { @@ -203,7 +201,8 @@ function $CompileProvider($provide) { } hasDirectives[name].push(directiveFactory); } else { - forEach(name, reverseParams(registerDirective)); + forEach(/** @type {Object.}*/(name), + reverseParams(/** @type {function(!(string|number), !Function):$CompileProvider} */(registerDirective))); } return this; }; @@ -215,7 +214,13 @@ function $CompileProvider($provide) { function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse, $controller, $rootScope) { - var Attributes = function(element, attr) { + + /** + * @param {!JQLite=} element Element the attributes belong to. + * @param {Object=} attr Initial attribute values. + * @constructor + */ + var Attributes = function Attributes(element, attr) { this.$$element = element; this.$attr = attr || {}; }; @@ -228,10 +233,11 @@ function $CompileProvider($provide) { * Set a normalized attribute on the element in a way such that all directives * can share the attribute. This function properly handles boolean attributes. * @param {string} key Normalized key. (ie ngAttribute) - * @param {string|boolean} value The value to set. If `null` attribute will be deleted. + * @param {string|boolean|undefined|null} value The value to set. If `null` or `undefined` + * attribute will be deleted. * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute. * Defaults to true. - * @param {string=} attrName Optional none normalized name. Defaults to key. + * @param {string=} attrName Optional denormalized attribute name. Defaults to key. */ $set: function(key, value, writeAttr, attrName) { var booleanKey = getBooleanAttrName(this.$$element[0], key), @@ -258,7 +264,9 @@ function $CompileProvider($provide) { if (value === null || value === undefined) { this.$$element.removeAttr(attrName); } else { - this.$$element.attr(attrName, value); + /** @type {JQLite} */ + var e = this.$$element; + e.attr(attrName, value); } } @@ -310,6 +318,12 @@ function $CompileProvider($provide) { //================================ + /** + * @param {!JQLite} $compileNodes + * @param transcludeFn + * @param {!number=} maxPriority + * @return {Function} + */ function compile($compileNodes, transcludeFn, maxPriority) { if (!($compileNodes instanceof jqLite)) { // jquery always rewraps, where as we need to preserve the original selector so that we can modify it. @@ -358,13 +372,14 @@ function $CompileProvider($provide) { * function, which is the a linking function for the node. * * @param {NodeList} nodeList an array of nodes to compile - * @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the + * @param {function(ng.Scope, ng.$compile.cloneAttachFn=)} transcludeFn A linking function, where the * scope argument is auto-generated to the new child of the transcluded parent scope. - * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then the + * @param {!JQLite=} $rootElement If the nodeList is the root of the compilation tree then the * rootElement must be set the jqLite collection of the compile root. This is * needed so that the jqLite collection items can be replaced with widgets. - * @param {number=} max directive priority - * @returns {?function} A composite linking function of all of the matched directives or null. + * @param {number=} maxPriority Up to what priority directives should be evaluated. + * @returns {?ng.$compile.compositeLinkFn} A composite linking function of all of the matched + * directives or null. */ function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority) { var linkFns = [], @@ -393,6 +408,9 @@ function $CompileProvider($provide) { // return a linking function if we have found anything, null otherwise return linkFnFound ? compositeLinkFn : null; + /** + * @type {ng.$compile.compositeLinkFn} + */ function compositeLinkFn(scope, nodeList, $rootElement, boundTranscludeFn) { var nodeLinkFn, childLinkFn, node, childScope, childTranscludeFn, i, ii, n; @@ -520,13 +538,13 @@ function $CompileProvider($provide) { * is responsible for inlining directive templates as well as terminating the application * of the directives if the terminal directive has been reached.. * - * @param {Array} directives Array of collected directives to execute their compile function. + * @param {!Array} directives Array of collected directives to execute their compile function. * this needs to be pre-sorted by priority order. - * @param {Node} compileNode The raw DOM node to apply the compile functions to - * @param {Object} templateAttrs The shared attribute function - * @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the + * @param {!Node} compileNode The raw DOM node to apply the compile functions to + * @param {!Object} templateAttrs The shared attribute function + * @param {!function(ng.Scope, ng.$compile.cloneAttachFn=)} transcludeFn A linking function, where the * scope argument is auto-generated to the new child of the transcluded parent scope. - * @param {DOMElement} $rootElement If we are working on the root of the compile tree then this + * @param {!JQLite=} $rootElement If we are working on the root of the compile tree then this * argument has the root jqLite array so that we can replace widgets on it. * @returns linkFn */ @@ -584,7 +602,7 @@ function $CompileProvider($provide) { $compileNode = templateAttrs.$$element = jqLite(document.createComment(' ' + directiveName + ': ' + templateAttrs[directiveName] + ' ')); compileNode = $compileNode[0]; - replaceWith($rootElement, jqLite($template[0]), compileNode); + replaceWith(/** @type {!JQLite} */($rootElement), jqLite($template[0]), compileNode); childTranscludeFn = compile($template, transcludeFn, terminalPriority); } else { $template = jqLite(JQLiteClone(compileNode)).contents(); @@ -608,7 +626,7 @@ function $CompileProvider($provide) { throw new Error(MULTI_ROOT_TEMPLATE_ERROR + directiveValue); } - replaceWith($rootElement, $compileNode, compileNode); + replaceWith(/** @type {!JQLite} */($rootElement), $compileNode, compileNode); var newTemplateAttrs = {$attr: {}}; @@ -833,6 +851,7 @@ function $CompileProvider($provide) { * looks up the directive and decorates it with exception handling and proper parameters. We * call this the boundDirective. * + * @param {Array} tDirectives Array of directives found in the template. * @param {string} name name of the directive to look up. * @param {string} location The directive must be found in specific format. * String containing any of theses characters: @@ -841,6 +860,7 @@ function $CompileProvider($provide) { * * `A': attribute * * `C`: class * * `M`: comment + * @param {!number=} maxPriority Up to what priority the directives should be evaluated. * @returns true if directive was added. */ function addDirective(tDirectives, name, location, maxPriority) { @@ -867,8 +887,8 @@ function $CompileProvider($provide) { * on the template need to be merged with the existing attributes in the DOM. * The desired effect is to have both of the attributes present. * - * @param {object} dst destination attributes (original DOM) - * @param {object} src source attributes (from the directive template) + * @param {Object} dst destination attributes (original DOM) + * @param {Object} src source attributes (from the directive template) */ function mergeTemplateAttributes(dst, src) { var srcAttr = src.$attr, @@ -1048,11 +1068,11 @@ function $CompileProvider($provide) { * This is a special jqLite.replaceWith, which can replace items which * have no parents, provided that the containing jqLite collection is provided. * - * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes - * in the root of the tree. - * @param {JqLite} $element The jqLite element which we are going to replace. We keep the shell, - * but replace its DOM node reference. - * @param {Node} newNode The new DOM node. + * @param {!JQLite} $rootElement The root of the compile tree. Used so that + * we can replace nodes in the root of the tree. + * @param {!JQLite} $element The jqLite element which we are going to + * replace. We keep the shell, but replace its DOM node reference. + * @param {!Node} newNode The new DOM node. */ function replaceWith($rootElement, $element, newNode) { var oldNode = $element[0], @@ -1078,6 +1098,12 @@ function $CompileProvider($provide) { }]; } +/** + * @type {Array.} + */ +$CompileProvider.$inject = ['$provide']; + + var PREFIX_REGEXP = /^(x[\:\-_]|data[\:\-_])/i; /** * Converts all accepted directives format into proper directive name. @@ -1110,7 +1136,7 @@ function directiveNormalize(name) { * @ngdoc property * @name ng.$compile.directive.Attributes#$attr * @propertyOf ng.$compile.directive.Attributes - * @returns {object} A map of DOM element attribute names to the normalized name. This is + * @returns {Object} A map of DOM element attribute names to the normalized name. This is * needed to do reverse lookup from normalized name back to actual name. */ @@ -1138,7 +1164,7 @@ function directiveNormalize(name) { */ function nodesetLinkingFn( - /* angular.Scope */ scope, + /* ng.Scope */ scope, /* NodeList */ nodeList, /* Element */ rootElement, /* function(Function) */ boundTranscludeFn @@ -1146,8 +1172,23 @@ function nodesetLinkingFn( function directiveLinkingFn( /* nodesetLinkingFn */ nodesetLinkingFn, - /* angular.Scope */ scope, + /* ng.Scope */ scope, /* Node */ node, /* Element */ rootElement, /* function(Function) */ boundTranscludeFn ){} + + +ng.$compile = {}; + +/** + * @typedef {function(Element, ng.Scope):undefined} + */ +ng.$compile.cloneAttachFn; + + +/** + * @typedef {function(!ng.Scope, !NodeList, !JQLite, !Function=)} + * scope, nodeList, $rootElement, boundTranscludeFn + */ +ng.$compile.compositeLinkFn; diff --git a/src/ng/controller.js b/src/ng/controller.js index e2f1b6ac7ce3..1fedffa8953e 100644 --- a/src/ng/controller.js +++ b/src/ng/controller.js @@ -9,6 +9,8 @@ * * This provider allows controller registration via the * {@link ng.$controllerProvider#register register} method. + * + * @constructor */ function $ControllerProvider() { var controllers = {}; @@ -18,13 +20,13 @@ function $ControllerProvider() { * @ngdoc function * @name ng.$controllerProvider#register * @methodOf ng.$controllerProvider - * @param {string} name Controller name + * @param {string|Object.} name Controller name * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI * annotations in the array notation). */ this.register = function(name, constructor) { if (isObject(name)) { - extend(controllers, name) + extend(controllers, /** @type Object */(name)) } else { controllers[name] = constructor; } @@ -58,7 +60,7 @@ function $ControllerProvider() { */ return function(constructor, locals) { if(isString(constructor)) { - var name = constructor; + var name = /** @type {string} */(constructor); constructor = controllers.hasOwnProperty(name) ? controllers[name] : getter(locals.$scope, name, true) || getter($window, name, true); diff --git a/src/ng/directive/booleanAttrs.js b/src/ng/directive/booleanAttrs.js index 2d1278cddb1c..44b43c668e75 100644 --- a/src/ng/directive/booleanAttrs.js +++ b/src/ng/directive/booleanAttrs.js @@ -304,6 +304,9 @@ * @param {string} expression Angular expression that will be evaluated. */ +/** + * @type {Object} + */ var ngAttributeAliasDirectives = {}; diff --git a/src/ng/directive/form.js b/src/ng/directive/form.js index 36b775cb3b40..b08fd2927f48 100644 --- a/src/ng/directive/form.js +++ b/src/ng/directive/form.js @@ -13,27 +13,25 @@ var nullFormCtrl = { * @ngdoc object * @name ng.directive:form.FormController * + * @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 ng.directive:form form} directive creates an instance + * of `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 containing 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 ng.directive:form form} directive creates an instance - * of `FormController`. - * + * @constructor */ -//asks for $scope to fool the BC controller module -FormController.$inject = ['$element', '$attrs', '$scope']; function FormController(element, attrs) { var form = this, parentForm = element.parent().controller('form') || nullFormCtrl, @@ -54,7 +52,11 @@ function FormController(element, attrs) { element.addClass(PRISTINE_CLASS); toggleValidCss(true); - // convenience method for easy toggling of classes + /** + * Convenience method for easy toggling of classes + * @param {boolean} isValid + * @param {string=} validationErrorKey + */ function toggleValidCss(isValid, validationErrorKey) { validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : ''; element. @@ -151,6 +153,10 @@ function FormController(element, attrs) { }; } +//asks for $scope to fool the BC controller module +FormController.$inject = ['$element', '$attrs', '$scope']; + + /** * @ngdoc directive @@ -257,6 +263,11 @@ function FormController(element, attrs) { */ + +/** + * @param {boolean=} isNgForm + * @return {Array} + */ var formDirectiveFactory = function(isNgForm) { return ['$timeout', function($timeout) { var formDirective = { diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js index aaabd1033398..61f407bc0b0b 100644 --- a/src/ng/directive/input.js +++ b/src/ng/directive/input.js @@ -5,374 +5,11 @@ 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 ng.directive:input.text - * - * @description - * Standard HTML text input with angular data binding. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required Adds `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. - * @param {string=} ngPattern 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=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trimming the - * input. - * - * @example - - - -
    - Single word: - - Required! - - Single word only! - - text = {{text}}
    - myForm.input.$valid = {{myForm.input.$valid}}
    - myForm.input.$error = {{myForm.input.$error}}
    - myForm.$valid = {{myForm.$valid}}
    - myForm.$error.required = {{!!myForm.$error.required}}
    -
    -
    - - 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'); - }); - - it('should not be trimmed', function() { - input('text').enter('untrimmed '); - expect(binding('text')).toEqual('untrimmed '); - expect(binding('myForm.input.$valid')).toEqual('true'); - }); - -
    - */ 'text': textInputType, - - - /** - * @ngdoc inputType - * @name ng.directive:input.number - * - * @description - * Text input with number validation and transformation. Sets the `number` validation - * error if not a valid number. - * - * @param {string} ngModel 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 {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. - * @param {string=} ngPattern 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=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - - - -
    - Number: - - Required! - - Not valid number! - value = {{value}}
    - myForm.input.$valid = {{myForm.input.$valid}}
    - myForm.input.$error = {{myForm.input.$error}}
    - myForm.$valid = {{myForm.$valid}}
    - myForm.$error.required = {{!!myForm.$error.required}}
    -
    -
    - - 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'); - }); - -
    - */ 'number': numberInputType, - - - /** - * @ngdoc inputType - * @name ng.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} ngModel 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 {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. - * @param {string=} ngPattern 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=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - - - -
    - URL: - - Required! - - Not valid url! - text = {{text}}
    - myForm.input.$valid = {{myForm.input.$valid}}
    - myForm.input.$error = {{myForm.input.$error}}
    - myForm.$valid = {{myForm.$valid}}
    - myForm.$error.required = {{!!myForm.$error.required}}
    - myForm.$error.url = {{!!myForm.$error.url}}
    -
    -
    - - 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'); - }); - -
    - */ 'url': urlInputType, - - - /** - * @ngdoc inputType - * @name ng.directive:input.email - * - * @description - * Text input with email validation. Sets the `email` validation error key if not a valid email - * address. - * - * @param {string} ngModel 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 {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. - * @param {string=} ngPattern 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 - - - -
    - Email: - - Required! - - Not valid email! - text = {{text}}
    - myForm.input.$valid = {{myForm.input.$valid}}
    - myForm.input.$error = {{myForm.input.$error}}
    - myForm.$valid = {{myForm.$valid}}
    - myForm.$error.required = {{!!myForm.$error.required}}
    - myForm.$error.email = {{!!myForm.$error.email}}
    -
    -
    - - 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'); - }); - -
    - */ 'email': emailInputType, - - - /** - * @ngdoc inputType - * @name ng.directive:input.radio - * - * @description - * HTML radio button. - * - * @param {string} ngModel 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=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - - - -
    - Red
    - Green
    - Blue
    - color = {{color}}
    -
    -
    - - it('should change state', function() { - expect(binding('color')).toEqual('blue'); - - input('color').select('red'); - expect(binding('color')).toEqual('red'); - }); - -
    - */ 'radio': radioInputType, - - - /** - * @ngdoc inputType - * @name ng.directive:input.checkbox - * - * @description - * HTML checkbox. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} ngTrueValue The value to which the expression should be set when selected. - * @param {string=} ngFalseValue The value to which the expression should be set when not selected. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - - - -
    - Value1:
    - Value2:
    - value1 = {{value1}}
    - value2 = {{value2}}
    -
    -
    - - 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'); - }); - -
    - */ 'checkbox': checkboxInputType, 'hidden': noop, @@ -387,7 +24,81 @@ function isEmpty(value) { } -function textInputType(scope, element, attr, ctrl, $sniffer, $browser) { +/** + * @ngdoc inputType + * @name ng.directive:input.text + * + * @description + * Standard HTML text input with angular data binding. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Adds `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. + * @param {string=} ngPattern 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=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trimming the + * input. + * + * @example + + + +
    + Single word: + Required! + Single word only! + + text = {{text}}
    + myForm.input.$valid = {{myForm.input.$valid}}
    + myForm.input.$error = {{myForm.input.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    +
    +
    + + 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'); + }); + + it('should not be trimmed', function() { + input('text').enter('untrimmed '); + expect(binding('text')).toEqual('untrimmed '); + expect(binding('myForm.input.$valid')).toEqual('true'); + }); + +
    + */ +ng.textInputType; + +function textInputType(scope, element, /** @type {ng.InputAttrs} */attr, ctrl, $sniffer, $browser) { var listener = function() { var value = element.val(); @@ -507,6 +218,77 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) { } } + +/** + * @ngdoc inputType + * @name ng.directive:input.number + * + * @description + * Text input with number validation and transformation. Sets the `number` validation + * error if not a valid number. + * + * @param {string} ngModel 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 {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. + * @param {string=} ngPattern 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=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
    + Number: + + Required! + + Not valid number! + value = {{value}}
    + myForm.input.$valid = {{myForm.input.$valid}}
    + myForm.input.$error = {{myForm.input.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    +
    +
    + + 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'); + }); + +
    + */ +ng.numberInputType; + function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) { textInputType(scope, element, attr, ctrl, $sniffer, $browser); @@ -569,6 +351,74 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) { }); } + +/** + * @ngdoc inputType + * @name ng.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} ngModel 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 {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. + * @param {string=} ngPattern 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=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
    + URL: + + Required! + + Not valid url! + text = {{text}}
    + myForm.input.$valid = {{myForm.input.$valid}}
    + myForm.input.$error = {{myForm.input.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    + myForm.$error.url = {{!!myForm.$error.url}}
    +
    +
    + + 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'); + }); + +
    + */ +ng.urlInputType; + function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) { textInputType(scope, element, attr, ctrl, $sniffer, $browser); @@ -586,6 +436,72 @@ function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) { ctrl.$parsers.push(urlValidator); } + +/** + * @ngdoc inputType + * @name ng.directive:input.email + * + * @description + * Text input with email validation. Sets the `email` validation error key if not a valid email + * address. + * + * @param {string} ngModel 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 {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. + * @param {string=} ngPattern 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 + + + +
    + Email: + + Required! + + Not valid email! + text = {{text}}
    + myForm.input.$valid = {{myForm.input.$valid}}
    + myForm.input.$error = {{myForm.input.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    + myForm.$error.email = {{!!myForm.$error.email}}
    +
    +
    + + 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'); + }); + +
    + */ +ng.emailInputType; + function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) { textInputType(scope, element, attr, ctrl, $sniffer, $browser); @@ -603,6 +519,47 @@ function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) { ctrl.$parsers.push(emailValidator); } + +/** + * @ngdoc inputType + * @name ng.directive:input.radio + * + * @description + * HTML radio button. + * + * @param {string} ngModel 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=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
    + Red
    + Green
    + Blue
    + color = {{color}}
    +
    +
    + + it('should change state', function() { + expect(binding('color')).toEqual('blue'); + + input('color').select('red'); + expect(binding('color')).toEqual('red'); + }); + +
    + */ +ng.radioInputType; + function radioInputType(scope, element, attr, ctrl) { // make the name unique, if not defined if (isUndefined(attr.name)) { @@ -625,6 +582,53 @@ function radioInputType(scope, element, attr, ctrl) { attr.$observe('value', ctrl.$render); } + +/** + * @ngdoc inputType + * @name ng.directive:input.checkbox + * + * @description + * HTML checkbox. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} ngTrueValue The value to which the expression should be set when selected. + * @param {string=} ngFalseValue The value to which the expression should be set when not selected. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
    + Value1:
    + Value2:
    + value1 = {{value1}}
    + value2 = {{value2}}
    +
    +
    + + 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'); + }); + +
    + */ +ng.checkboxInputType; + function checkboxInputType(scope, element, attr, ctrl) { var trueValue = attr.ngTrueValue, falseValue = attr.ngFalseValue; @@ -775,6 +779,8 @@ function checkboxInputType(scope, element, attr, ctrl) { */ +ng.inputDirective; + var inputDirective = ['$browser', '$sniffer', function($browser, $sniffer) { return { restrict: 'E', @@ -929,7 +935,11 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ $element.addClass(PRISTINE_CLASS); toggleValidCss(true); - // convenience method for easy toggling of classes + /** + * Convenience method for easy toggling of classes + * @param {boolean} isValid + * @param {string=} validationErrorKey + */ function toggleValidCss(isValid, validationErrorKey) { validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : ''; $element. @@ -1248,6 +1258,8 @@ var requiredDirective = function() { */ +ng.ngListDirective; + var ngListDirective = function() { return { require: 'ngModel', @@ -1300,3 +1312,9 @@ var ngValueDirective = function() { } }; }; + + +/** + * @typedef {{ngFalseValue, ngTrueValue, ngPattern, ngTrim}} + */ +ng.InputAttrs; diff --git a/src/ng/directive/ngBind.js b/src/ng/directive/ngBind.js index 0c99f27ddaeb..d74e363d69ab 100644 --- a/src/ng/directive/ngBind.js +++ b/src/ng/directive/ngBind.js @@ -22,7 +22,7 @@ * * * @element ANY - * @param {expression} ngBind {@link guide/expression Expression} to evaluate. + * @param {string} ngBind {@link guide/expression Expression} to evaluate. * * @example * Enter a name in the Live Preview text box; the greeting below the text box changes instantly. @@ -47,6 +47,8 @@ */ +ng.ngBindDirective; + var ngBindDirective = ngDirective(function(scope, element, attr) { element.addClass('ng-binding').data('$binding', attr.ngBind); scope.$watch(attr.ngBind, function ngBindWatchAction(value) { @@ -102,6 +104,8 @@ var ngBindDirective = ngDirective(function(scope, element, attr) { */ +ng.ngBindTemplateDirective; + var ngBindTemplateDirective = ['$interpolate', function($interpolate) { return function(scope, element, attr) { // TODO: move this to scenario runner @@ -127,13 +131,15 @@ var ngBindTemplateDirective = ['$interpolate', function($interpolate) { * See {@link ngSanitize.$sanitize $sanitize} docs for examples. * * @element ANY - * @param {expression} ngBindHtmlUnsafe {@link guide/expression Expression} to evaluate. + * @param {string} ngBindHtmlUnsafe {@link guide/expression Expression} to evaluate. */ -var ngBindHtmlUnsafeDirective = [function() { +ng.ngBindHtmlUnsafeDirective; + +var ngBindHtmlUnsafeDirective = function() { return function(scope, element, attr) { element.addClass('ng-binding').data('$binding', attr.ngBindHtmlUnsafe); scope.$watch(attr.ngBindHtmlUnsafe, function ngBindHtmlUnsafeWatchAction(value) { element.html(value || ''); }); }; -}]; +}; diff --git a/src/ng/directive/ngClass.js b/src/ng/directive/ngClass.js index 79c55d7abdd6..f1139566c372 100644 --- a/src/ng/directive/ngClass.js +++ b/src/ng/directive/ngClass.js @@ -69,7 +69,7 @@ function classDirective(name, selector) { * new classes are added. * * @element ANY - * @param {expression} ngClass {@link guide/expression Expression} to eval. The result + * @param {string} ngClass {@link guide/expression 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. * @@ -119,7 +119,7 @@ var ngClassDirective = classDirective('', true); * {@link ng.directive:ngRepeat ngRepeat}. * * @element ANY - * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result + * @param {string} ngClassOdd {@link guide/expression Expression} to eval. The result * of the evaluation can be a string representing space delimited class names or an array. * * @example @@ -166,7 +166,7 @@ var ngClassOddDirective = classDirective('Odd', 0); * {@link ng.directive:ngRepeat ngRepeat}. * * @element ANY - * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The + * @param {string} ngClassEven {@link guide/expression Expression} to eval. The * result of the evaluation can be a string representing space delimited class names or an array. * * @example diff --git a/src/ng/directive/ngCloak.js b/src/ng/directive/ngCloak.js index baa7afa8b71f..02dc52f91573 100644 --- a/src/ng/directive/ngCloak.js +++ b/src/ng/directive/ngCloak.js @@ -10,7 +10,7 @@ * directive to avoid the undesirable flicker effect caused by the html template display. * * The directive can be applied to the `` element, but typically a fine-grained application is - * prefered in order to benefit from progressive rendering of the browser view. + * preferred in order to benefit from progressive rendering of the browser view. * * `ngCloak` works in cooperation with a css rule that is embedded within `angular.js` and * `angular.min.js` files. Following is the css rule: diff --git a/src/ng/directive/ngController.js b/src/ng/directive/ngController.js index b6619d319426..c38b59dcf408 100644 --- a/src/ng/directive/ngController.js +++ b/src/ng/directive/ngController.js @@ -20,7 +20,7 @@ * * @element ANY * @scope - * @param {expression} ngController Name of a globally accessible constructor function or an + * @param {string} ngController Name of a globally accessible constructor function or an * {@link guide/expression expression} that on the current scope evaluates to a * constructor function. * @@ -95,9 +95,11 @@ */ -var ngControllerDirective = [function() { +ng.ngController; + +var ngControllerDirective = function() { return { scope: true, controller: '@' }; -}]; +}; diff --git a/src/ng/directive/ngEventDirs.js b/src/ng/directive/ngEventDirs.js index e2aedd7ff509..9a9a17adf370 100644 --- a/src/ng/directive/ngEventDirs.js +++ b/src/ng/directive/ngEventDirs.js @@ -9,7 +9,7 @@ * element is clicked. * * @element ANY - * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon + * @param {string} ngClick {@link guide/expression Expression} to evaluate upon * click. (Event object is available as `$event`) * * @example @@ -29,6 +29,8 @@ */ +ng.ngClick; + /* * A directive that allows creation of custom onclick handlers that are defined as angular * expressions and are compiled and executed within the current scope. @@ -93,7 +95,7 @@ forEach( * Specify custom behavior on mouseup event. * * @element ANY - * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon + * @param {string} ngMouseup {@link guide/expression Expression} to evaluate upon * mouseup. (Event object is available as `$event`) * * @example @@ -207,7 +209,7 @@ forEach( * server and reloading the current page). * * @element form - * @param {expression} ngSubmit {@link guide/expression Expression} to eval. + * @param {string} ngSubmit {@link guide/expression Expression} to eval. * * @example diff --git a/src/ng/directive/ngInclude.js b/src/ng/directive/ngInclude.js index d4eacbe3e0af..0954bef6f379 100644 --- a/src/ng/directive/ngInclude.js +++ b/src/ng/directive/ngInclude.js @@ -83,7 +83,7 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile' return { restrict: 'ECA', terminal: true, - compile: function(element, attr) { + compile: function(element, /** @type {ng.ngIncludeAttrs;}*/ attr) { var srcExp = attr.ngInclude || attr.src, onloadExp = attr.onload || '', autoScrollExp = attr.autoscroll; @@ -129,3 +129,9 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile' } }; }]; + + +/** + * @typedef {{autoscroll}} + */ +ng.ngIncludeAttrs; diff --git a/src/ng/directive/ngInit.js b/src/ng/directive/ngInit.js index d157f3967d51..a3ca84403cf0 100644 --- a/src/ng/directive/ngInit.js +++ b/src/ng/directive/ngInit.js @@ -9,7 +9,7 @@ * before the template enters execution mode during bootstrap. * * @element ANY - * @param {expression} ngInit {@link guide/expression Expression} to eval. + * @param {string} ngInit {@link guide/expression Expression} to eval. * * @example diff --git a/src/ng/directive/ngPluralize.js b/src/ng/directive/ngPluralize.js index 3938d4e39378..d7135942266d 100644 --- a/src/ng/directive/ngPluralize.js +++ b/src/ng/directive/ngPluralize.js @@ -86,7 +86,7 @@ * 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} 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. * @@ -169,6 +169,9 @@ */ +ng.ngPluralize; + + var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) { var BRACE = /{}/g; return { diff --git a/src/ng/directive/ngRepeat.js b/src/ng/directive/ngRepeat.js index c59fefacc956..851746294f97 100644 --- a/src/ng/directive/ngRepeat.js +++ b/src/ng/directive/ngRepeat.js @@ -20,7 +20,7 @@ * @element ANY * @scope * @priority 1000 - * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. Two + * @param {string} ngRepeat An 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` diff --git a/src/ng/directive/ngShowHide.js b/src/ng/directive/ngShowHide.js index 74195468915b..03b8e7b2582f 100644 --- a/src/ng/directive/ngShowHide.js +++ b/src/ng/directive/ngShowHide.js @@ -9,7 +9,7 @@ * conditionally. * * @element ANY - * @param {expression} ngShow If the {@link guide/expression expression} is truthy + * @param {string} ngShow If the {@link guide/expression expression} is truthy * then the element is shown or hidden respectively. * * @example @@ -49,7 +49,7 @@ var ngShowDirective = ngDirective(function(scope, element, attr){ * conditionally. * * @element ANY - * @param {expression} ngHide If the {@link guide/expression expression} is truthy then + * @param {string} ngHide If the {@link guide/expression expression} is truthy then * the element is shown or hidden respectively. * * @example diff --git a/src/ng/directive/ngStyle.js b/src/ng/directive/ngStyle.js index a1c2b699ebb4..9eb77afe47cf 100644 --- a/src/ng/directive/ngStyle.js +++ b/src/ng/directive/ngStyle.js @@ -8,7 +8,7 @@ * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally. * * @element ANY - * @param {expression} ngStyle {@link guide/expression Expression} which evals to an + * @param {string} ngStyle {@link guide/expression Expression} which evals to an * object whose keys are CSS style names and values are corresponding values for those CSS * keys. * diff --git a/src/ng/directive/ngSwitch.js b/src/ng/directive/ngSwitch.js index 24c6047ac809..e9bcdc1e81c2 100644 --- a/src/ng/directive/ngSwitch.js +++ b/src/ng/directive/ngSwitch.js @@ -1,5 +1,7 @@ 'use strict'; +var NG_SWITCH = 'ng-switch'; + /** * @ngdoc directive * @name ng.directive:ngSwitch @@ -8,11 +10,22 @@ * @description * Conditionally change the DOM structure. * - * @usageContent - * ... - * ... - * ... - * ... + * @usage + * + * ... + * ... + * ... + * ... + * + * + * or + * + * + * ... + * ... + * ... + * ... + * * * @scope * @param {*} ngSwitch|on expression to match against ng-switch-when. @@ -59,14 +72,13 @@ */ -var NG_SWITCH = 'ng-switch'; var ngSwitchDirective = valueFn({ restrict: 'EA', require: 'ngSwitch', - controller: function ngSwitchController() { + controller: /** @constructor */function ngSwitchController() { this.cases = {}; }, - link: function(scope, element, attr, ctrl) { + link: function(scope, element, /** @type {ng.ngSwitch} */attr, ctrl) { var watchExpr = attr.ngSwitch || attr.on, selectedTransclude, selectedElement, @@ -111,3 +123,8 @@ var ngSwitchDefaultDirective = ngDirective({ }; } }); + +/** + * @typedef {{on, change, ngSwitch}} + */ +ng.ngSwitchAttrs; diff --git a/src/ng/directive/script.js b/src/ng/directive/script.js index 16bd7d15b7d9..b75b92ae89ee 100644 --- a/src/ng/directive/script.js +++ b/src/ng/directive/script.js @@ -8,8 +8,8 @@ * Load content of a script tag, with type `text/ng-template`, into `$templateCache`, so that the * template can be used by `ngInclude`, `ngView` or directive templates. * - * @restrict E - * @param {'text/ng-template'} type must be set to `'text/ng-template'` + * @restrict A + * @param {string} type must be set to `'text/ng-template'` * * @example @@ -28,7 +28,15 @@ }); + * + * @usage + */ +ng.scriptDirective; + var scriptDirective = ['$templateCache', function($templateCache) { return { restrict: 'E', diff --git a/src/ng/directive/select.js b/src/ng/directive/select.js index d82bd139c3b2..934381c7094b 100644 --- a/src/ng/directive/select.js +++ b/src/ng/directive/select.js @@ -32,7 +32,7 @@ * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of * `required` when you want to data-bind to the `required` attribute. - * @param {comprehension_expression=} ngOptions in one of the following forms: + * @param {string=} ngOptions collection comprehension expression in one of the following forms: * * * for array data sources: * * `label` **`for`** `value` **`in`** `array` diff --git a/src/ng/document.js b/src/ng/document.js index f1f2d334ff7b..c454aa21414c 100644 --- a/src/ng/document.js +++ b/src/ng/document.js @@ -8,6 +8,8 @@ * @description * A {@link angular.element jQuery (lite)}-wrapped reference to the browser's `window.document` * element. + * + * @constructor */ function $DocumentProvider(){ this.$get = ['$window', function(window){ diff --git a/src/ng/exceptionHandler.js b/src/ng/exceptionHandler.js index f74e09eead3a..84c35fa349ca 100644 --- a/src/ng/exceptionHandler.js +++ b/src/ng/exceptionHandler.js @@ -1,26 +1,30 @@ 'use strict'; /** - * @ngdoc function - * @name ng.$exceptionHandler - * @requires $log - * - * @description - * Any uncaught exception in angular expressions is delegated to this service. - * The default implementation simply delegates to `$log.error` which logs it into - * the browser console. - * - * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by - * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing. - * - * @param {Error} exception Exception associated with the error. - * @param {string=} cause optional information about the context in which - * the error was thrown. - * + * @constructor */ function $ExceptionHandlerProvider() { - this.$get = ['$log', function($log){ - return function(exception, cause) { + this.$get = ['$log', function($log) { + + /** + * @ngdoc function + * @name ng.$exceptionHandler + * @requires $log + * + * @description + * Any uncaught exception in angular expressions is delegated to this service. + * The default implementation simply delegates to `$log.error` which logs it into + * the browser console. + * + * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by + * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing. + * + * @param {Error} exception Exception associated with the error. + * @param {string=} cause optional information about the context in which + * the error was thrown. + * + */ + return function $exceptionHandler(exception, cause) { $log.error.apply($log, arguments); }; }]; diff --git a/src/ng/filter.js b/src/ng/filter.js index e3ccb72ee2b6..89d23e2b3d0f 100644 --- a/src/ng/filter.js +++ b/src/ng/filter.js @@ -48,6 +48,7 @@ * {@link guide/dev_guide.templates.filters Understanding Angular Filters} in the angular Developer * Guide. */ + /** * @ngdoc method * @name ng.$filterProvider#register @@ -55,8 +56,8 @@ * @description * Register filter factory function. * - * @param {String} name Name of the filter. - * @param {function} fn The filter factory function which is injectable. + * @param {string} name Name of the filter. + * @param {Function} fn The filter factory function which is injectable. */ @@ -71,11 +72,17 @@ * * {{ expression | [ filter_name ] }} * - * @param {String} name Name of the filter function to retrieve + * @param {string} name Name of the filter function to retrieve * @return {Function} the filter function */ -$FilterProvider.$inject = ['$provide']; -function $FilterProvider($provide) { +ng.$Filter; + +var $FilterProvider = ['$provide', + /** + * @constructor + * @param $provide + */ + function $FilterProvider_($provide) { var suffix = 'Filter'; function register(name, factory) { @@ -91,13 +98,13 @@ function $FilterProvider($provide) { //////////////////////////////////////// - register('currency', currencyFilter); - register('date', dateFilter); - register('filter', filterFilter); - register('json', jsonFilter); - register('limitTo', limitToFilter); - register('lowercase', lowercaseFilter); - register('number', numberFilter); - register('orderBy', orderByFilter); - register('uppercase', uppercaseFilter); -} + register('currency', currencyFilterFactory); + register('date', dateFilterFactory); + register('filter', filterFilterFactory); + register('json', jsonFilterFactory); + register('limitTo', limitToFilterFactory); + register('lowercase', lowercaseFilterFactory); + register('number', numberFilterFactory); + register('orderBy', orderByFilterFactory); + register('uppercase', uppercaseFilterFactory); +}]; diff --git a/src/ng/filter/filter.js b/src/ng/filter/filter.js index 467f6699f394..93a0d70287d8 100644 --- a/src/ng/filter/filter.js +++ b/src/ng/filter/filter.js @@ -1,86 +1,87 @@ 'use strict'; -/** - * @ngdoc filter - * @name ng.filter:filter - * @function - * - * @description - * Selects a subset of items from `array` and returns it as a new array. - * - * Note: This function is used to augment the `Array` type in Angular expressions. See - * {@link ng.$filter} for more information about Angular arrays. - * - * @param {Array} array The source array. - * @param {string|Object|function()} expression The predicate to be used for selecting items from - * `array`. - * - * Can be one of: - * - * - `string`: Predicate that results in a substring match using the value of `expression` - * string. All strings or objects with string properties in `array` that contain this string - * will be returned. The predicate can be negated by prefixing the string with `!`. - * - * - `Object`: A pattern object can be used to filter specific properties on objects contained - * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items - * which have property `name` containing "M" and property `phone` containing "1". A special - * property name `$` can be used (as in `{$:"text"}`) to accept a match against any - * property of the object. That's equivalent to the simple substring match with a `string` - * as described above. - * - * - `function`: A predicate function can be used to write arbitrary filters. The function is - * called for each element of `array`. The final result is an array of those elements that - * the predicate returned true for. - * - * @example - - -
    +function filterFilterFactory() { - Search: - - - - - - -
    NamePhone
    {{friend.name}}{{friend.phone}}
    -
    - Any:
    - Name only
    - Phone only
    - - - - - - -
    NamePhone
    {{friend.name}}{{friend.phone}}
    -
    - - it('should search across all fields when filtering with a string', function() { - input('searchText').enter('m'); - expect(repeater('#searchTextResults tr', 'friend in friends').column('friend.name')). - toEqual(['Mary', 'Mike', 'Adam']); + /** + * @ngdoc filter + * @name ng.filter:filter + * @function + * + * @description + * Selects a subset of items from `array` and returns it as a new array. + * + * Note: This function is used to augment the `Array` type in Angular expressions. See + * {@link ng.$filter} for more information about Angular arrays. + * + * @param {Array} array The source array. + * @param {string|Object|function()} expression The predicate to be used for selecting items from + * `array`. + * + * Can be one of: + * + * - `string`: Predicate that results in a substring match using the value of `expression` + * string. All strings or objects with string properties in `array` that contain this string + * will be returned. The predicate can be negated by prefixing the string with `!`. + * + * - `Object`: A pattern object can be used to filter specific properties on objects contained + * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items + * which have property `name` containing "M" and property `phone` containing "1". A special + * property name `$` can be used (as in `{$:"text"}`) to accept a match against any + * property of the object. That's equivalent to the simple substring match with a `string` + * as described above. + * + * - `function`: A predicate function can be used to write arbitrary filters. The function is + * called for each element of `array`. The final result is an array of those elements that + * the predicate returned true for. + * + * @example + + +
    - input('searchText').enter('76'); - expect(repeater('#searchTextResults tr', 'friend in friends').column('friend.name')). - toEqual(['John', 'Julie']); - }); + Search: + + + + + + +
    NamePhone
    {{friend.name}}{{friend.phone}}
    +
    + Any:
    + Name only
    + Phone only
    + + + + + + +
    NamePhone
    {{friend.name}}{{friend.phone}}
    +
    + + it('should search across all fields when filtering with a string', function() { + input('searchText').enter('m'); + expect(repeater('#searchTextResults tr', 'friend in friends').column('friend.name')). + toEqual(['Mary', 'Mike', 'Adam']); - it('should search in specific fields when filtering with a predicate object', function() { - input('search.$').enter('i'); - expect(repeater('#searchObjResults tr', 'friend in friends').column('friend.name')). - toEqual(['Mary', 'Mike', 'Julie']); - }); - -
    - */ -function filterFilter() { + input('searchText').enter('76'); + expect(repeater('#searchTextResults tr', 'friend in friends').column('friend.name')). + toEqual(['John', 'Julie']); + }); + + it('should search in specific fields when filtering with a predicate object', function() { + input('search.$').enter('i'); + expect(repeater('#searchObjResults tr', 'friend in friends').column('friend.name')). + toEqual(['Mary', 'Mike', 'Julie']); + }); +
    +
    + */ return function(array, expression) { if (!(array instanceof Array)) return array; var predicates = []; diff --git a/src/ng/filter/filters.js b/src/ng/filter/filters.js index 4680a30681e4..b165b56fbf1b 100644 --- a/src/ng/filter/filters.js +++ b/src/ng/filter/filters.js @@ -1,71 +1,75 @@ 'use strict'; -/** - * @ngdoc filter - * @name ng.filter:currency - * @function - * - * @description - * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default - * symbol for current locale is used. - * - * @param {number} amount Input to filter. - * @param {string=} symbol Currency symbol or identifier to be displayed. - * @returns {string} Formatted number. - * - * - * @example - - - -
    -
    - default currency symbol ($): {{amount | currency}}
    - custom currency identifier (USD$): {{amount | currency:"USD$"}} -
    -
    - - it('should init with 1234.56', function() { - expect(binding('amount | currency')).toBe('$1,234.56'); - expect(binding('amount | currency:"USD$"')).toBe('USD$1,234.56'); - }); - it('should update', function() { - input('amount').enter('-1234'); - expect(binding('amount | currency')).toBe('($1,234.00)'); - expect(binding('amount | currency:"USD$"')).toBe('(USD$1,234.00)'); - }); - -
    - */ -currencyFilter.$inject = ['$locale']; -function currencyFilter($locale) { +var currencyFilterFactory = ['$locale', function currencyFilterFactory_($locale) { var formats = $locale.NUMBER_FORMATS; - return function(amount, currencySymbol){ + + /** + * @ngdoc filter + * @name ng.filter:currency + * @function + * + * @description + * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default + * symbol for current locale is used. + * + * @param {number} amount Input to filter. + * @param {string=} currencySymbol Currency symbol or identifier to be displayed. + * @returns {string} Formatted number. + * + * + * @example + + + +
    +
    + default currency symbol ($): {{amount | currency}}
    + custom currency identifier (USD$): {{amount | currency:"USD$"}} +
    +
    + + it('should init with 1234.56', function() { + expect(binding('amount | currency')).toBe('$1,234.56'); + expect(binding('amount | currency:"USD$"')).toBe('USD$1,234.56'); + }); + it('should update', function() { + input('amount').enter('-1234'); + expect(binding('amount | currency')).toBe('($1,234.00)'); + expect(binding('amount | currency:"USD$"')).toBe('(USD$1,234.00)'); + }); + +
    + */ + return function currencyFilter(amount, currencySymbol){ if (isUndefined(currencySymbol)) currencySymbol = formats.CURRENCY_SYM; return formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, 2). replace(/\u00A4/g, currencySymbol); }; -} +}]; -/** - * @ngdoc filter - * @name ng.filter:number - * @function - * - * @description - * Formats a number as text. - * - * If the input is not a number an empty string is returned. - * - * @param {number|string} number Number to format. - * @param {(number|string)=} [fractionSize=2] Number of decimal places to round the number to. - * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit. - * - * @example + +var numberFilterFactory = ['$locale', function numberFilterFactory_($locale) { + var formats = $locale.NUMBER_FORMATS; + + /** + * @ngdoc filter + * @name ng.filter:number + * @function + * + * @description + * Formats a number as text. + * + * If the input is not a number an empty string is returned. + * + * @param {number|string} number Number to format. + * @param {(number|string)=} [fractionSize=2] Number of decimal places to round the number to. + * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit. + * + * @example -
    - Limit {{numbers}} to: -

    Output numbers: {{ numbers | limitTo:numLimit }}

    - Limit {{letters}} to: -

    Output letters: {{ letters | limitTo:letterLimit }}

    -
    -
    - - it('should limit the number array to first three items', function() { - expect(element('.doc-example-live input[ng-model=numLimit]').val()).toBe('3'); - expect(element('.doc-example-live input[ng-model=letterLimit]').val()).toBe('3'); - expect(binding('numbers | limitTo:numLimit')).toEqual('[1,2,3]'); - expect(binding('letters | limitTo:letterLimit')).toEqual('abc'); - }); +function limitToFilterFactory() { + /** + * @ngdoc function + * @name ng.filter:limitTo + * @function + * + * @description + * Creates a new array or string containing only a specified number of elements. The elements + * are taken from either the beginning or the end of the source array or string, as specified by + * the value and sign (positive or negative) of `limit`. + * + * Note: This function is used to augment the `Array` type in Angular expressions. See + * {@link ng.$filter} for more information about Angular arrays. + * + * @template T + * @param {T} input Source array or string to be limited. + * @param {string|number} limit The length of the returned array or string. If the `limit` number + * is positive, `limit` number of items from the beginning of the source array/string are copied. + * If the number is negative, `limit` number of items from the end of the source array/string + * are copied. The `limit` will be trimmed if it exceeds `array.length` + * @returns {!T} A new sub-array or substring of length `limit` or less if input array + * had less than `limit` elements. + * + * @example + + + +
    + Limit {{numbers}} to: +

    Output numbers: {{ numbers | limitTo:numLimit }}

    + Limit {{letters}} to: +

    Output letters: {{ letters | limitTo:letterLimit }}

    +
    +
    + + it('should limit the number array to first three items', function() { + expect(element('.doc-example-live input[ng-model=numLimit]').val()).toBe('3'); + expect(element('.doc-example-live input[ng-model=letterLimit]').val()).toBe('3'); + expect(binding('numbers | limitTo:numLimit')).toEqual('[1,2,3]'); + expect(binding('letters | limitTo:letterLimit')).toEqual('abc'); + }); - it('should update the output when -3 is entered', function() { - input('numLimit').enter(-3); - input('letterLimit').enter(-3); - expect(binding('numbers | limitTo:numLimit')).toEqual('[7,8,9]'); - expect(binding('letters | limitTo:letterLimit')).toEqual('ghi'); - }); + it('should update the output when -3 is entered', function() { + input('numLimit').enter(-3); + input('letterLimit').enter(-3); + expect(binding('numbers | limitTo:numLimit')).toEqual('[7,8,9]'); + expect(binding('letters | limitTo:letterLimit')).toEqual('ghi'); + }); - it('should not exceed the maximum size of input array', function() { - input('numLimit').enter(100); - input('letterLimit').enter(100); - expect(binding('numbers | limitTo:numLimit')).toEqual('[1,2,3,4,5,6,7,8,9]'); - expect(binding('letters | limitTo:letterLimit')).toEqual('abcdefghi'); - }); - -
    - */ -function limitToFilter(){ - return function(input, limit) { + it('should not exceed the maximum size of input array', function() { + input('numLimit').enter(100); + input('letterLimit').enter(100); + expect(binding('numbers | limitTo:numLimit')).toEqual('[1,2,3,4,5,6,7,8,9]'); + expect(binding('letters | limitTo:letterLimit')).toEqual('abcdefghi'); + }); +
    +
    + */ + return function limitToFilter(input, limit) { if (!isArray(input) && !isString(input)) return input; - + limit = int(limit); if (isString(input)) { diff --git a/src/ng/filter/orderBy.js b/src/ng/filter/orderBy.js index 93f3f5a6977d..d654f8216269 100644 --- a/src/ng/filter/orderBy.js +++ b/src/ng/filter/orderBy.js @@ -1,97 +1,98 @@ 'use strict'; -/** - * @ngdoc function - * @name ng.filter:orderBy - * @function - * - * @description - * Orders a specified `array` by the `expression` predicate. - * - * Note: this function is used to augment the `Array` type in Angular expressions. See - * {@link ng.$filter} for more informaton about Angular arrays. - * - * @param {Array} array The array to sort. - * @param {function(*)|string|Array.<(function(*)|string)>} expression A predicate to be - * used by the comparator to determine the order of elements. - * - * Can be one of: - * - * - `function`: Getter function. The result of this function will be sorted using the - * `<`, `=`, `>` operator. - * - `string`: An Angular expression which evaluates to an object to order by, such as 'name' - * to sort by a property called 'name'. Optionally prefixed with `+` or `-` to control - * ascending or descending sort order (for example, +name or -name). - * - `Array`: An array of function or string predicates. The first predicate in the array - * is used for sorting, but when two items are equivalent, the next predicate is used. - * - * @param {boolean=} reverse Reverse the order the array. - * @returns {Array} Sorted copy of the source array. - * - * @example - - - -
    -
    Sorting predicate = {{predicate}}; reverse = {{reverse}}
    -
    - [ unsorted ] - - - - - - - - - - - -
    Name - (^)Phone NumberAge
    {{friend.name}}{{friend.phone}}{{friend.age}}
    -
    -
    - - it('should be reverse ordered by aged', function() { - expect(binding('predicate')).toBe('-age'); - expect(repeater('table.friend', 'friend in friends').column('friend.age')). - toEqual(['35', '29', '21', '19', '10']); - expect(repeater('table.friend', 'friend in friends').column('friend.name')). - toEqual(['Adam', 'Julie', 'Mike', 'Mary', 'John']); - }); +var orderByFilterFactory = ['$parse', function orderByFilterFact($parse){ - it('should reorder the table when user selects different predicate', function() { - element('.doc-example-live a:contains("Name")').click(); - expect(repeater('table.friend', 'friend in friends').column('friend.name')). - toEqual(['Adam', 'John', 'Julie', 'Mary', 'Mike']); - expect(repeater('table.friend', 'friend in friends').column('friend.age')). - toEqual(['35', '10', '29', '19', '21']); + /** + * @ngdoc function + * @name ng.filter:orderBy + * @function + * + * @description + * Orders a specified `array` by the `expression` predicate. + * + * Note: this function is used to augment the `Array` type in Angular expressions. See + * {@link ng.$filter} for more informaton about Angular arrays. + * + * @template T + * @param {Array.} array The array to sort. + * @param {function(T):number|string|Array.<(function(T):number|string)>} sortPredicate A predicate to be + * used by the comparator to determine the order of elements. + * + * Can be one of: + * + * - `function`: Getter function. The result of this function will be sorted using the + * `<`, `=`, `>` operator. + * - `string`: An Angular expression which evaluates to an object to order by, such as 'name' + * to sort by a property called 'name'. Optionally prefixed with `+` or `-` to control + * ascending or descending sort order (for example, +name or -name). + * - `Array`: An array of function or string predicates. The first predicate in the array + * is used for sorting, but when two items are equivalent, the next predicate is used. + * + * @param {boolean=} reverseOrder Reverse the order the array. + * @returns {Array.} Sorted copy of the source array. + * + * @example + + + +
    +
    Sorting predicate = {{predicate}}; reverse = {{reverse}}
    +
    + [ unsorted ] + + + + + + + + + + + +
    Name + (^)Phone NumberAge
    {{friend.name}}{{friend.phone}}{{friend.age}}
    +
    +
    + + it('should be reverse ordered by aged', function() { + expect(binding('predicate')).toBe('-age'); + expect(repeater('table.friend', 'friend in friends').column('friend.age')). + toEqual(['35', '29', '21', '19', '10']); + expect(repeater('table.friend', 'friend in friends').column('friend.name')). + toEqual(['Adam', 'Julie', 'Mike', 'Mary', 'John']); + }); - element('.doc-example-live a:contains("Phone")').click(); - expect(repeater('table.friend', 'friend in friends').column('friend.phone')). - toEqual(['555-9876', '555-8765', '555-5678', '555-4321', '555-1212']); - expect(repeater('table.friend', 'friend in friends').column('friend.name')). - toEqual(['Mary', 'Julie', 'Adam', 'Mike', 'John']); - }); - -
    - */ -orderByFilter.$inject = ['$parse']; -function orderByFilter($parse){ - return function(array, sortPredicate, reverseOrder) { + it('should reorder the table when user selects different predicate', function() { + element('.doc-example-live a:contains("Name")').click(); + expect(repeater('table.friend', 'friend in friends').column('friend.name')). + toEqual(['Adam', 'John', 'Julie', 'Mary', 'Mike']); + expect(repeater('table.friend', 'friend in friends').column('friend.age')). + toEqual(['35', '10', '29', '19', '21']); + + element('.doc-example-live a:contains("Phone")').click(); + expect(repeater('table.friend', 'friend in friends').column('friend.phone')). + toEqual(['555-9876', '555-8765', '555-5678', '555-4321', '555-1212']); + expect(repeater('table.friend', 'friend in friends').column('friend.name')). + toEqual(['Mary', 'Julie', 'Adam', 'Mike', 'John']); + }); +
    +
    + */ + return function orderByFilter(array, sortPredicate, reverseOrder) { if (!(array instanceof Array)) return array; if (!sortPredicate) return array; - sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate]; + sortPredicate = /** @type {!Array} */ (isArray(sortPredicate) ? sortPredicate: [sortPredicate]); sortPredicate = map(sortPredicate, function(predicate){ var descending = false, get = predicate || identity; if (isString(predicate)) { @@ -134,4 +135,4 @@ function orderByFilter($parse){ } } } -} +}]; diff --git a/src/ng/http.js b/src/ng/http.js index 65d2ee5debd2..c79b492bd29b 100644 --- a/src/ng/http.js +++ b/src/ng/http.js @@ -72,7 +72,7 @@ function isSameDomain(requestUrl, locationUrl) { * Headers are lazy parsed when first requested. * @see parseHeaders * - * @param {(string|Object)} headers Headers to provide access to. + * @param {string} headers Headers to provide access to. * @returns {function(string=)} Returns a getter function which if called with: * * - if called with single an argument returns a single header value or null @@ -81,6 +81,9 @@ function isSameDomain(requestUrl, locationUrl) { function headersGetter(headers) { var headersObj = isObject(headers) ? headers : undefined; + /** + * @param {string=} name The header name. + */ return function(name) { if (!headersObj) headersObj = parseHeaders(headers); @@ -100,12 +103,12 @@ function headersGetter(headers) { * * @param {*} data Data to transform. * @param {function(string=)} headers Http headers getter fn. - * @param {(function|Array.)} fns Function or an array of functions. + * @param {(Function|Array.)} fns Function or an array of functions. * @returns {*} Transformed data. */ function transformData(data, headers, fns) { if (isFunction(fns)) - return fns(data, headers); + return /** @type {Function} */(fns)(data, headers); forEach(fns, function(fn) { data = fn(data, headers); @@ -120,6 +123,9 @@ function isSuccess(status) { } +/** + * @constructor + */ function $HttpProvider() { var JSON_START = /^\s*(\[|\{[^\{])/, JSON_END = /[\}\]]\s*$/, @@ -132,7 +138,7 @@ function $HttpProvider() { // strip json vulnerability protection prefix data = data.replace(PROTECTION_PREFIX, ''); if (JSON_START.test(data) && JSON_END.test(data)) - data = fromJson(data, true); + data = fromJson(data); } return data; }], @@ -403,7 +409,7 @@ function $HttpProvider() { * properties of either $httpProvider.defaults, or the per-request config object. * * - * @param {object} config Object describing the request to be made and how it should be + * @param {Object} config Object describing the request to be made and how it should be * processed. The object has following properties: * * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc) @@ -431,7 +437,7 @@ function $HttpProvider() { * - **responseType** - `{string}` - see {@link * https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType requestType}. * - * @returns {HttpPromise} Returns a {@link ng.$q promise} object with the + * @returns {ng.$HttpPromise} Returns a {@link ng.$q promise} object with the * standard `then` method and two http specific methods: `success` and `error`. The `then` * method takes two arguments a success and an error callback which will be called with a * response object. The `success` and `error` methods take a single argument - a function that @@ -687,6 +693,9 @@ function $HttpProvider() { return $http; + /** + * @param {...string} names + */ function createShortMethods(names) { forEach(arguments, function(name) { $http[name] = function(url, config) { @@ -699,7 +708,10 @@ function $HttpProvider() { } - function createShortMethodsWithData(name) { + /** + * @param {...string} names + */ + function createShortMethodsWithData(names) { forEach(arguments, function(name) { $http[name] = function(url, data, config) { return $http(extend(config || {}, { @@ -828,3 +840,13 @@ function $HttpProvider() { }]; } + + +/** + * @typedef {{ + * then: Function, + * success: Function, + * error: Function + * }} + */ +ng.$HttpPromise; diff --git a/src/ng/httpBackend.js b/src/ng/httpBackend.js index bca46ee1a939..d344a1fd9ffc 100644 --- a/src/ng/httpBackend.js +++ b/src/ng/httpBackend.js @@ -22,6 +22,8 @@ var XHR = window.XMLHttpRequest || function() { * * During testing this implementation is swapped with {@link ngMock.$httpBackend mock * $httpBackend} which can be trained with responses. + * + * @constructor */ function $HttpBackendProvider() { this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) { @@ -89,6 +91,12 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument, } + /** + * @param {Function} callback + * @param {number} status + * @param {string=} response + * @param {string=} headersString + */ function completeRequest(callback, status, response, headersString) { // URL_MATCH is defined in src/service/location.js var protocol = (url.match(URL_MATCH) || ['', locationProtocol])[1]; diff --git a/src/ng/interpolate.js b/src/ng/interpolate.js index dcf05d778f3e..bfe45f2bb09d 100644 --- a/src/ng/interpolate.js +++ b/src/ng/interpolate.js @@ -6,8 +6,9 @@ * @function * * @description - * * Used for configuring the interpolation markup. Defaults to `{{` and `}}`. + * + * @constructor */ function $InterpolateProvider() { var startSymbol = '{{'; @@ -21,7 +22,7 @@ function $InterpolateProvider() { * Symbol to denote start of expression in the interpolated string. Defaults to `{{`. * * @param {string=} value new value to set the starting symbol to. - * @returns {string|self} Returns the symbol when used as getter and self if used as setter. + * @returns {string|$InterpolateProvider} Returns the symbol when used as getter and self if used as setter. */ this.startSymbol = function(value){ if (value) { @@ -40,7 +41,7 @@ function $InterpolateProvider() { * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`. * * @param {string=} value new value to set the ending symbol to. - * @returns {string|self} Returns the symbol when used as getter and self if used as setter. + * @returns {string|$InterpolateProvider} Returns the symbol when used as getter and self if used as setter. */ this.endSymbol = function(value){ if (value) { @@ -81,8 +82,8 @@ function $InterpolateProvider() { * @param {string} text The text with markup to interpolate. * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have * embedded expression in order to return an interpolation function. Strings with no - * embedded expression will return null for the interpolation function. - * @returns {function(context)} an interpolation function which is used to compute the interpolated + * embedded expression will return undefined instead of an interpolation function. + * @returns {function(ng.Scope)|undefined} an interpolation function which is used to compute the interpolated * string. The function has these parameters: * * * `context`: an object against which any expressions embedded in the strings are evaluated @@ -123,11 +124,11 @@ function $InterpolateProvider() { if (!mustHaveExpression || hasInterpolation) { concat.length = length; - fn = function(context) { + fn = function(/** @type {ng.Scope} */scope) { try { for(var i = 0, ii = length, part; i=} search New search params - string or hash object + * @param {string|Object.=} search New search params - string or hash object * @param {string=} paramValue If `search` is a string, then `paramValue` will override only a * single search parameter. If the value is `null`, the parameter will be deleted. * - * @return {string} search + * @return {string|ng.core.Location} search */ search: function(search, paramValue) { if (isUndefined(search)) @@ -358,7 +366,7 @@ LocationUrl.prototype = { this.$$search[search] = paramValue; } } else { - this.$$search = isString(search) ? parseKeyValue(search) : search; + this.$$search = isString(search) ? parseKeyValue(/** @type {string} */(search)) : search; } this.$$compose(); @@ -399,6 +407,11 @@ LocationUrl.prototype = { LocationHashbangUrl.prototype = inherit(LocationUrl.prototype); +/** + * @constructor + * @implements {ng.core.Location} + * @extends {LocationHashbangUrl} + */ function LocationHashbangInHtml5Url(url, hashPrefix, appBaseUrl, baseExtra) { LocationHashbangUrl.apply(this, arguments); @@ -466,6 +479,8 @@ function locationGetterSetter(property, preprocess) { * @name ng.$locationProvider * @description * Use the `$locationProvider` to configure how the application deep linking paths are stored. + * + * @constructor */ function $LocationProvider(){ var hashPrefix = '', @@ -480,7 +495,7 @@ function $LocationProvider(){ * @returns {*} current value if used as getter or itself (chaining) if used as setter */ this.hashPrefix = function(prefix) { - if (isDefined(prefix)) { + if (prefix !== undefined) { hashPrefix = prefix; return this; } else { @@ -616,3 +631,89 @@ function $LocationProvider(){ } }]; } + + +ng.core = {}; + +/** + * @interface + */ +ng.core.Location = function() {}; + + +/** + * @protected + * @param {!string} url + */ +ng.core.Location.$$parse = function(url) {}; + + +/** + * @protected + */ +ng.core.Location.$$compose = function() {}; + + +/** + * @protected + */ +ng.core.Location.$$replace; + + +/** + * @return {!string} full url + */ +ng.core.Location.absUrl = function() {}; + + +/** + * @param {string=} url + * @return {ng.core.Location} url + */ +ng.core.Location.url = function(url) {}; + + +/** + * @return {!string} protocol of current url + */ +ng.core.Location.protocol = function() {}; + + +/** + * @return {!string} + */ +ng.core.Location.host = function() {}; + + +/** + * @return {!number} + */ +ng.core.Location.port = function() {}; + + +/** + * @param {string=} path + * @return {string} + */ +ng.core.Location.path = function(path) {}; + + +/** + * @param {!(string|Object.)=} search + * @param {!string=} paramValue + * @return {!(string|ng.core.Location)} + */ +ng.core.Location.search = function(search, paramValue) {}; + + +/** + * @param {!string=} hash New hash fragment + * @return {!string} hash + */ +ng.core.Location.hash = function(hash) {}; + + +/** + * @return {!ng.core.Location} + */ +ng.core.Location.replace = function() {}; diff --git a/src/ng/log.js b/src/ng/log.js index 2a58d442399a..39f01fa4bcaa 100644 --- a/src/ng/log.js +++ b/src/ng/log.js @@ -38,11 +38,13 @@ * @name ng.$logProvider * @description * Use the `$logProvider` to configure how the application logs messages + * + * @constructor */ function $LogProvider(){ var debug = true, self = this; - + /** * @ngdoc property * @name ng.$logProvider#debugEnabled @@ -59,7 +61,7 @@ function $LogProvider(){ return debug; } }; - + this.$get = ['$window', function($window){ return { /** @@ -101,18 +103,18 @@ function $LogProvider(){ * Write an error message */ error: consoleLog('error'), - + /** * @ngdoc method * @name ng.$log#debug * @methodOf ng.$log - * + * * @description * Write a debug message */ debug: (function () { var fn = consoleLog('debug'); - + return function() { if (debug) { fn.apply(self, arguments); @@ -121,17 +123,17 @@ function $LogProvider(){ }()) }; - function formatError(arg) { - if (arg instanceof Error) { - if (arg.stack) { - arg = (arg.message && arg.stack.indexOf(arg.message) === -1) - ? 'Error: ' + arg.message + '\n' + arg.stack - : arg.stack; - } else if (arg.sourceURL) { - arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line; + function formatError(error) { + if (error instanceof Error) { + if (error.stack) { + error = (error.message && error.stack.indexOf(error.message) === -1) + ? 'Error: ' + error.message + '\n' + error.stack + : error.stack; + } else if (error.sourceURL) { + error = error.message + '\n' + error.sourceURL + ':' + error['line']; } } - return arg; + return error; } function consoleLog(type) { diff --git a/src/ng/parse.js b/src/ng/parse.js index 5a70979a7b8e..4e811e3d59ba 100644 --- a/src/ng/parse.js +++ b/src/ng/parse.js @@ -101,9 +101,13 @@ function lex(text, csp){ return chars.indexOf(lastCh) != -1; } + /** + * @param {number=} [i=1] + * @return {?string} + */ function peek(i) { var num = i || 1; - return index + num < text.length ? text.charAt(index + num) : false; + return index + num < text.length ? text.charAt(index + num) : null; } function isNumber(ch) { return '0' <= ch && ch <= '9'; @@ -121,6 +125,11 @@ function lex(text, csp){ return ch == '-' || ch == '+' || isNumber(ch); } + /** + * @param {string} error + * @param {number=} start + * @param {number=} end + */ function throwError(error, start, end) { end = end || index; throw Error("Lexer Error: " + error + " at column" + @@ -306,8 +315,12 @@ function parser(text, json, $filter, csp){ return value; /////////////////////////////////// + /** + * @param msg {string} + * @param token {{text: string, index: number}} + */ function throwError(msg, token) { - throw Error("Syntax Error: Token '" + token.text + + throw new Error("Syntax Error: Token '" + token.text + "' " + msg + " at column " + (token.index + 1) + " of the expression [" + text + "] starting at [" + text.substring(token.index) + "]."); @@ -319,6 +332,13 @@ function parser(text, json, $filter, csp){ return tokens[0]; } + /** + * @param {string=} e1 + * @param {string=} e2 + * @param {string=} e3 + * @param {string=} e4 + * @return {({text: string, index: number}|undefined)} + */ function peek(e1, e2, e3, e4) { if (tokens.length > 0) { var token = tokens[0]; @@ -328,9 +348,14 @@ function parser(text, json, $filter, csp){ return token; } } - return false; } + /** + * @param {string=} e1 + * @param {string=} e2 + * @param {string=} e3 + * @param {string=} e4 + */ function expect(e1, e2, e3, e4){ var token = peek(e1, e2, e3, e4); if (token) { @@ -540,7 +565,7 @@ function parser(text, json, $filter, csp){ context = primary; primary = fieldAccess(primary); } else { - throwError("IMPOSSIBLE"); + throwError("!!!", {}); // should never happen } } return primary; @@ -676,7 +701,7 @@ function setter(obj, path, setValue) { * Return the value accesible from the object by path. Any undefined traversals are ignored * @param {Object} obj starting object * @param {string} path path to traverse - * @param {boolean=true} bindFnToScope + * @param {boolean=} [bindFnToScope=true] * @returns value as accesbile by path */ //TODO(misko): this function needs to be removed @@ -856,6 +881,10 @@ function getterFn(path, csp) { * allows one to set values to expressions. * */ + +/** + * @constructor + */ function $ParseProvider() { var cache = {}; this.$get = ['$filter', '$sniffer', function($filter, $sniffer) { diff --git a/src/ng/q.js b/src/ng/q.js index ef856dca6572..868c679e6439 100644 --- a/src/ng/q.js +++ b/src/ng/q.js @@ -123,31 +123,35 @@ * you can treat promises attached to a scope as if they were the resulting values. * - Q has many more features that $q, but that comes at a cost of bytes. $q is tiny, but contains * all the important functionality needed for common async tasks. - * + * * # Testing - * + * *
      *    it('should simulate promise', inject(function($q, $rootSCope) {
      *      var deferred = $q.defer();
      *      var promise = deferred.promise;
      *      var resolvedValue;
    - * 
    + *
      *      promise.then(function(value) { resolvedValue = value; });
      *      expect(resolvedValue).toBeUndefined();
    - * 
    + *
      *      // Simulate resolving of promise
      *      deferred.resolve(123);
      *      // Note that the 'then' function does not get called synchronously.
      *      // This is because we want the promise API to always be async, whether or not
      *      // it got called synchronously or asynchronously.
      *      expect(resolvedValue).toBeUndefined();
    - * 
    + *
      *      // Propagate promise resolution to 'then' functions using $apply().
      *      $rootScope.$apply();
      *      expect(resolvedValue).toEqual(123);
      *    });
      *  
    */ + +/** + * @constructor + */ function $QProvider() { this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) { @@ -161,10 +165,10 @@ function $QProvider() { /** * Constructs a promise manager. * - * @param {function(function)} nextTick Function for executing functions in the next turn. - * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for + * @param {function(Function)} nextTick Function for executing functions in the next turn. + * @param {function(...[*])} exceptionHandler Function into which unexpected exceptions are passed for * debugging purposes. - * @returns {object} Promise manager. + * @returns {Object} Promise manager. */ function qFactory(nextTick, exceptionHandler) { @@ -175,7 +179,7 @@ function qFactory(nextTick, exceptionHandler) { * @description * Creates a `Deferred` object which represents a task which will finish in the future. * - * @returns {Deferred} Returns a new instance of deferred. + * @returns {ng.Deferred} Returns a new instance of deferred. */ var defer = function() { var pending = [], @@ -291,7 +295,7 @@ function qFactory(nextTick, exceptionHandler) { * * * @param {*} reason Constant, message, exception or an object representing the rejection reason. - * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`. + * @returns {ng.Promise} Returns a promise that was already resolved as rejected with the `reason`. */ var reject = function(reason) { return { @@ -316,8 +320,8 @@ function qFactory(nextTick, exceptionHandler) { * the promise comes from a source that can't be trusted. * * @param {*} value Value or a promise - * @returns {Promise} Returns a single promise that will be resolved with an array of values, - * each value corresponding to the promise at the same index in the `promises` array. If any of + * @returns {ng.Promise} Returns a single promise that will be resolved with an array of values, + * each value coresponding to the promise at the same index in the `promises` array. If any of * the promises is resolved with a rejection, this resulting promise will be resolved with the * same rejection. */ @@ -377,8 +381,8 @@ function qFactory(nextTick, exceptionHandler) { * Combines multiple promises into a single promise that is resolved when all of the input * promises are resolved. * - * @param {Array.} promises An array of promises. - * @returns {Promise} Returns a single promise that will be resolved with an array of values, + * @param {Array.} promises An array of promises. + * @returns {ng.Promise} Returns a single promise that will be resolved with an array of values, * each value corresponding to the promise at the same index in the `promises` array. If any of * the promises is resolved with a rejection, this resulting promise will be resolved with the * same rejection. @@ -413,3 +417,19 @@ function qFactory(nextTick, exceptionHandler) { all: all }; } + + +/** + * @typedef {{then: Function}} + */ +ng.Promise; + + +/** + * @typedef {{ + * promise: ng.Promise, + * resolve: function(*): undefined, + * reject: function(*): undefined + * }} + */ +ng.Deferred; diff --git a/src/ng/rootScope.js b/src/ng/rootScope.js index 549517b66ad9..2c32ac8cc86e 100644 --- a/src/ng/rootScope.js +++ b/src/ng/rootScope.js @@ -58,6 +58,10 @@ * All other scopes are child scopes of the root scope. Scopes provide mechanism for watching the model and provide * event processing life-cycle. See {@link guide/scope developer guide on scopes}. */ + +/** + * @constructor + */ function $RootScopeProvider(){ var TTL = 10; @@ -119,14 +123,7 @@ function $RootScopeProvider(){ expect(parent.salutation).toEqual('Hello'); * * - * - * @param {Object.=} providers Map of service factory which need to be provided - * for the current scope. Defaults to {@link ng}. - * @param {Object.=} instanceCache Provides pre-instantiated services which should - * append/override services provided by `providers`. This is handy when unit-testing and having - * the need to override a default service. - * @returns {Object} Newly created scope. - * + * @constructor */ function Scope() { this.$id = nextUid(); @@ -167,12 +164,12 @@ function $RootScopeProvider(){ * the scope and its child scopes to be permanently detached from the parent and thus stop * participating in model change detection and listener notification by invoking. * - * @param {boolean} isolate if true then the scope does not prototypically inherit from the + * @param {!boolean=} isolate if true then the scope does not prototypically inherit from the * parent scope. The scope is isolated, as it can not see parent scope properties. * When creating widgets it is useful for the widget to not accidentally read parent * state. * - * @returns {Object} The newly created child scope. + * @returns {ng.Scope} The newly created child scope. * */ $new: function(isolate) { @@ -187,6 +184,10 @@ function $RootScopeProvider(){ child = new Scope(); child.$root = this.$root; } else { + /** + * @constructor + * @extends {ng.Scope} + */ Child = function() {}; // should be anonymous; This is so that when the minifier munges // the name it does not become random set of chars. These will then show up as class // name in the debugger. @@ -205,7 +206,7 @@ function $RootScopeProvider(){ } else { this.$$childHead = this.$$childTail = child; } - return child; + return /** @type ng.Scope */ (child); }, /** @@ -267,14 +268,14 @@ function $RootScopeProvider(){ * * * - * @param {(function()|string)} watchExpression Expression that is evaluated on each + * @param {(function()|string)} watchExp Expression that is evaluated on each * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers a * call to the `listener`. * * - `string`: Evaluated as {@link guide/expression expression} * - `function(scope)`: called with current `scope` as a parameter. * @param {(function()|string)=} listener Callback called whenever the return value of - * the `watchExpression` changes. + * the `watchExp` changes. * * - `string`: Evaluated as {@link guide/expression expression} * - `function(newValue, oldValue, scope)`: called with current and previous values as parameters. @@ -507,11 +508,13 @@ function $RootScopeProvider(){ expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3); * * - * @param {(string|function())=} expression An angular expression to be executed. + * @param {(string|function())} expr An angular expression to be executed. * * - `string`: execute using the rules as defined in {@link guide/expression expression}. * - `function(scope)`: execute the function with the current `scope` parameter. * + * @param {Object=} locals Map of local variables to be used during expression evaluation + * * @returns {*} The result of evaluating the expression. */ $eval: function(expr, locals) { @@ -536,7 +539,7 @@ function $RootScopeProvider(){ * Any exceptions from the execution of the expression are forwarded to the * {@link ng.$exceptionHandler $exceptionHandler} service. * - * @param {(string|function())=} expression An angular expression to be executed. + * @param {(string|function())} expr An angular expression to be executed. * * - `string`: execute using the rules as defined in {@link guide/expression expression}. * - `function(scope)`: execute the function with the current `scope` parameter. @@ -585,7 +588,7 @@ function $RootScopeProvider(){ * was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method. * * - * @param {(string|function())=} exp An angular expression to be executed. + * @param {(string|function())=} expr An angular expression to be executed. * * - `string`: execute using the rules as defined in {@link guide/expression expression}. * - `function(scope)`: execute the function with current `scope` parameter. @@ -620,7 +623,8 @@ function $RootScopeProvider(){ * event life cycle. * * @param {string} name Event name to listen on. - * @param {function(event, args...)} listener Function to call when the event is emitted. + * @param {function((ng.ScopeBroadcastEvent|ng.ScopeEmitEvent), ...[*])} listener Function to + * call when the event is emitted. * @returns {function()} Returns a deregistration function for this listener. * * The event listener function format is: `function(event, args...)`. The `event` object @@ -662,21 +666,22 @@ function $RootScopeProvider(){ * Afterwards, the event traverses upwards toward the root scope and calls all registered * listeners along the way. The event will stop propagating if one of the listeners cancels it. * - * Any exception emmited from the {@link ng.$rootScope.Scope#$on listeners} will be passed + * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed * onto the {@link ng.$exceptionHandler $exceptionHandler} service. * * @param {string} name Event name to emit. * @param {...*} args Optional set of arguments which will be passed onto the event listeners. - * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on} + * @return {ng.ScopeEmitEvent} Event object, see {@link ng.$rootScope.Scope#$on} */ $emit: function(name, args) { var empty = [], namedListeners, scope = this, stopPropagation = false, + /** @type {ng.ScopeEmitEvent} */ event = { name: name, - targetScope: scope, + targetScope: /** @type ng.Scope */(scope), // TODO(i): remove type cast stopPropagation: function() {stopPropagation = true;}, preventDefault: function() { event.defaultPrevented = true; @@ -733,15 +738,16 @@ function $RootScopeProvider(){ * * @param {string} name Event name to emit. * @param {...*} args Optional set of arguments which will be passed onto the event listeners. - * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on} + * @return {ng.ScopeBroadcastEvent} Event object, see {@link ng.$rootScope.Scope#$on} */ $broadcast: function(name, args) { var target = this, current = target, next = target, + /** @type ng.ScopeBroadcastEvent */ event = { name: name, - targetScope: target, + targetScope: /** @type ng.Scope */ (target), // TODO(i): remove type cast preventDefault: function() { event.defaultPrevented = true; }, @@ -815,3 +821,43 @@ function $RootScopeProvider(){ function initWatchVal() {} }]; } + +/** + * @typedef {{ + * $id: string, + * $watch: function((function()|string), (function()|string)=, boolean=):(function():undefined), + * $digest: function():undefined, + * $apply: function((string|Function)=), + * $new: function(!boolean=):ng.Scope, + * $destroy: function():undefined, + * $eval: function((string|function())): *, + * $evalAsync: function((string|function())): undefined, + * $on: function(string, function((ng.ScopeBroadcastEvent|ng.ScopeEmitEvent), ...[*])):(function():undefined), + * $emit: function(string, ...[*]): ng.ScopeEmitEvent, + * $broadcast: function(string, ...[*]): ng.ScopeBroadcastEvent + * }} + */ +ng.Scope; + + +/** + * @typedef {{ + * name: string, + * targetScope: ng.Scope, + * preventDefault: function(): undefined, + * defaultPrevented: boolean + * }} + */ +ng.ScopeBroadcastEvent; + + +/** + * @typedef {{ + * name: string, + * targetScope: ng.Scope, + * stopPropagation: function(): undefined, + * preventDefault: function(): undefined, + * defaultPrevented: boolean + * }} + */ +ng.ScopeEmitEvent; diff --git a/src/ng/route.js b/src/ng/route.js index 971caa1ccbdb..f0079d8cba5e 100644 --- a/src/ng/route.js +++ b/src/ng/route.js @@ -7,10 +7,12 @@ * @function * * @description - * * Used for configuring routes. See {@link ng.$route $route} for an example. + * + * @constructor */ function $RouteProvider(){ + /** @type Object. */ var routes = {}; /** @@ -35,6 +37,7 @@ function $RouteProvider(){ * - `controller` – `{(string|function()=}` – Controller fn that should be associated with newly * created scope or the name of a {@link angular.Module#controller registered controller} * if passed as a string. + * * - `template` – `{string=|function()=}` – html template as a string or function that returns * an html template as a string which should be used by {@link ng.directive:ngView ngView} or * {@link ng.directive:ngInclude ngInclude} directives. @@ -59,7 +62,7 @@ function $RouteProvider(){ * `$routeChangeSuccess` event is fired. The map object is: * * - `key` – `{string}`: a name of a dependency to be injected into the controller. - * - `factory` - `{string|function}`: If `string` then it is an alias for a service. + * - `factory` - `{string|Function}`: If `string` then it is an alias for a service. * Otherwise if function, then it is {@link api/AUTO.$injector#invoke injected} * and the return value is treated as the dependency. If the result is a promise, it is resolved * before its value is injected into the controller. @@ -116,7 +119,7 @@ function $RouteProvider(){ * @returns {Object} self */ this.otherwise = function(params) { - this.when(null, params); + this.when('null', params); return this; }; @@ -452,7 +455,9 @@ function $RouteProvider(){ /** - * @returns the current active route, by matching it against the URL + * @returns {{controller, template, templateUrl, resolve, reloadOnSearch, redirectTo, params, + * pathParams}} the currently active route, found by matching routedefs against + * $location.path() */ function parseRoute() { // Match a route diff --git a/src/ng/routeParams.js b/src/ng/routeParams.js index 0202f8e5b043..be1d15662cba 100644 --- a/src/ng/routeParams.js +++ b/src/ng/routeParams.js @@ -25,6 +25,10 @@ * $routeParams ==> {chapterId:1, sectionId:2, search:'moby'} * */ + +/** + * @constructor + */ function $RouteParamsProvider() { this.$get = valueFn({}); } diff --git a/src/ng/sniffer.js b/src/ng/sniffer.js index 9342fbd51b57..0eb4b885798a 100644 --- a/src/ng/sniffer.js +++ b/src/ng/sniffer.js @@ -7,11 +7,15 @@ * @requires $window * @requires $document * - * @property {boolean} history Does the browser support html5 history api ? - * @property {boolean} hashchange Does the browser support hashchange event ? - * * @description * This is very simple implementation of testing browser's features. + * + * @property {boolean} history Does the browser support html5 history api ? + * @property {boolean} hashchange Does the browser support hashchange event ? + */ + +/** + * @constructor */ function $SnifferProvider() { this.$get = ['$window', '$document', function($window, $document) { diff --git a/src/ng/timeout.js b/src/ng/timeout.js index e92bca69a7bb..aec2a3c0478d 100644 --- a/src/ng/timeout.js +++ b/src/ng/timeout.js @@ -1,6 +1,9 @@ 'use strict'; +/** + * @constructor + */ function $TimeoutProvider() { this.$get = ['$rootScope', '$browser', '$q', '$exceptionHandler', function($rootScope, $browser, $q, $exceptionHandler) { @@ -29,7 +32,7 @@ function $TimeoutProvider() { * @param {number=} [delay=0] Delay in milliseconds. * @param {boolean=} [invokeApply=true] If set to false skips model dirty checking, otherwise * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. - * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this + * @returns {ng.Promise} Promise that will be resolved when the timeout is reached. The value this * promise will be resolved with is the return value of the `fn` function. */ function timeout(fn, delay, invokeApply) { @@ -70,7 +73,7 @@ function $TimeoutProvider() { * Cancels a task associated with the `promise`. As a result of this the promise will be * resolved with a rejection. * - * @param {Promise=} promise Promise returned by the `$timeout` function. + * @param {ng.Promise} promise Promise returned by the `$timeout` function. * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully * canceled. */ diff --git a/src/ng/window.js b/src/ng/window.js index a23d23069506..b048ec93a2f4 100644 --- a/src/ng/window.js +++ b/src/ng/window.js @@ -23,6 +23,10 @@ */ + +/** + * @constructor + */ function $WindowProvider(){ this.$get = valueFn(window); } diff --git a/src/ngSanitize/sanitize.js b/src/ngSanitize/sanitize.js index 04eafa0d77dc..f4dc1d7e4cb2 100644 --- a/src/ngSanitize/sanitize.js +++ b/src/ngSanitize/sanitize.js @@ -183,7 +183,7 @@ function makeMap(str) { * }); * * @param {string} html string - * @param {object} handler + * @param {Object} handler */ function htmlParser( html, handler ) { var index, chars, match, stack = [], last = html; @@ -339,7 +339,7 @@ function encodeEntities(value) { /** * create an HTML/XML writer which writes to buffer * @param {Array} buf use buf.jain('') to get out sanitized html string - * @returns {object} in the form of { + * @returns {Object} in the form of { * start: function(tag, attrs, unary) {}, * end: function(tag) {}, * chars: function(text) {}, diff --git a/src/ngScenario/Describe.js b/src/ngScenario/Describe.js index 4d52e9d58486..d2acdf71e436 100644 --- a/src/ngScenario/Describe.js +++ b/src/ngScenario/Describe.js @@ -128,7 +128,7 @@ angular.scenario.Describe.prototype.xit = angular.noop; * Gets an array of functions representing all the tests (recursively). * that can be executed with SpecRunner's. * - * @return {Array} Array of it blocks { + * @return {Array.} Array of it blocks { * definition : Object // parent Describe * only: boolean * name: string diff --git a/src/ngScenario/ObjectModel.js b/src/ngScenario/ObjectModel.js index 9c6ce56c23f4..f4b17b25a4cd 100644 --- a/src/ngScenario/ObjectModel.js +++ b/src/ngScenario/ObjectModel.js @@ -155,7 +155,7 @@ angular.scenario.ObjectModel.prototype.emit = function(eventName) { * this spec. * * @param spec Spec to compute the path for. - * @return {Array} The describe block path + * @return {Array.} The describe block path */ angular.scenario.ObjectModel.prototype.getDefinitionPath = function(spec) { var path = []; @@ -182,7 +182,7 @@ angular.scenario.ObjectModel.prototype.getSpec = function(id) { * * @param {string} id Id of the spec * @param {string} name Name of the spec - * @param {Array=} definitionNames List of all describe block names that wrap this spec + * @param {Array.=} definitionNames List of all describe block names that wrap this spec */ angular.scenario.ObjectModel.Spec = function(id, name, definitionNames) { this.id = id; diff --git a/src/ngScenario/Scenario.js b/src/ngScenario/Scenario.js index 4833e62954b3..2fdfab3fcd40 100644 --- a/src/ngScenario/Scenario.js +++ b/src/ngScenario/Scenario.js @@ -227,7 +227,7 @@ function callerFile(offset) { * Triggers a browser event. Attempts to choose the right event if one is * not specified. * - * @param {Object} element Either a wrapped jQuery/jqLite node or a DOMElement + * @param {Object} element Either a wrapped jQuery/jqLite node or a Element * @param {string} type Optional event type. * @param {Array.=} keys Optional list of pressed keys * (valid values: 'alt', 'meta', 'shift', 'ctrl') diff --git a/src/ngScenario/angular-bootstrap.js b/src/ngScenario/angular-senario-dev.js similarity index 97% rename from src/ngScenario/angular-bootstrap.js rename to src/ngScenario/angular-senario-dev.js index a0012ff7fb5d..8339769aeeb4 100644 --- a/src/ngScenario/angular-bootstrap.js +++ b/src/ngScenario/angular-senario-dev.js @@ -35,7 +35,7 @@ 'var _jQuery = jQuery.noConflict(true);' + '' ); - addScript("../angular-bootstrap.js"); + addScript("../angular-dev.js"); addScript("Scenario.js"); addScript("Application.js"); diff --git a/test/bootstrap/code.html b/test/bootstrap/code.html index e1eaa8d2d265..2e8dcb6e188b 100644 --- a/test/bootstrap/code.html +++ b/test/bootstrap/code.html @@ -1,7 +1,7 @@ - + + diff --git a/test/testabilityPatch.js b/test/testabilityPatch.js index d55ff0159ca3..1f50455c614f 100644 --- a/test/testabilityPatch.js +++ b/test/testabilityPatch.js @@ -85,7 +85,7 @@ function dealoc(obj) { } /** - * @param {DOMElement} element + * @param {Element} element * @param {boolean=} showNgClass */ function sortedHtml(element, showNgClass) {