diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Angular.js | 48 | ||||
| -rw-r--r-- | src/AngularPublic.js | 8 | ||||
| -rw-r--r-- | src/Browser.js | 155 | ||||
| -rw-r--r-- | src/services.js | 402 |
4 files changed, 568 insertions, 45 deletions
diff --git a/src/Angular.js b/src/Angular.js index e917b3ee..5a5a4ab3 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -315,7 +315,6 @@ var _undefined = undefined, */ angularValidator = extensionMap(angular, 'validator'), - /** * @ngdoc overview * @name angular.filter @@ -473,6 +472,53 @@ var _undefined = undefined, * }); */ angularFormatter = extensionMap(angular, 'formatter'), + + /** + * @ngdoc overview + * @name angular.service + * + * @description + * # Overview + * Services are substituable objects, which are wired together using dependency injection. + * Each service could have dependencies (other services), which are passed in constructor. + * Because JS is dynamicaly typed language, dependency injection can not use static types + * to satisfy these dependencies, so each service must explicitely define its dependencies. + * This is done by $inject property. + * + * For now, life time of all services is the same as the life time of page. + * + * + * # Standard services + * The Angular framework provides a standard set of services for common operations. + * You can write your own services and rewrite these standard services as well. + * Like other core angular variables, standard services always start with $. + * + * * `angular.service.$window` + * * `angular.service.$document` + * * `angular.service.$location` + * * `angular.service.$log` + * * `angular.service.$exceptionHandler` + * * `angular.service.$hover` + * * `angular.service.$invalidWidgets` + * * `angular.service.$route` + * * `angular.service.$xhr` + * * `angular.service.$xhr.error` + * * `angular.service.$xhr.bulk` + * * `angular.service.$xhr.cache` + * * `angular.service.$resource` + * * `angular.service.$cookies` + * * `angular.service.$cookieStore` + * + * # Writing your own services + * <pre> + * angular.service('notify', function(location) { + * this.one = function() { + * } + * }, {$inject: ['$location']}); + * </pre> + * + * # Using services in controller + */ angularService = extensionMap(angular, 'service'), angularCallbacks = extensionMap(angular, 'callbacks'), nodeName, diff --git a/src/AngularPublic.js b/src/AngularPublic.js index 2b5d4fbc..c241238c 100644 --- a/src/AngularPublic.js +++ b/src/AngularPublic.js @@ -1,4 +1,12 @@ var browserSingleton; +/** + * @ngdoc service + * @name angular.service.$browser + * @requires $log + * + * @description + * Represents the browser. + */ angularService('$browser', function($log){ if (!browserSingleton) { browserSingleton = new Browser( diff --git a/src/Browser.js b/src/Browser.js index bf9b5483..2608f160 100644 --- a/src/Browser.js +++ b/src/Browser.js @@ -19,7 +19,20 @@ function Browser(location, document, head, XHR, $log) { var outstandingRequestCount = 0; var outstandingRequestCallbacks = []; - self.xhr = function(method, url, post, callback){ + /** + * @ngdoc method + * @name angular.service.$browser#xhr + * @methodOf angular.service.$browser + * + * @param {string} method Requested method (get|post|put|delete|head|json) + * @param {string} url Requested url + * @param {string=} post Post data to send + * @param {function(number, string)} callback Function that will be called on response + * + * @description + * Send ajax request + */ + self.xhr = function(method, url, post, callback) { if (isFunction(post)) { callback = post; post = _null; @@ -63,7 +76,14 @@ function Browser(location, document, head, XHR, $log) { } }; - self.notifyWhenNoOutstandingRequests = function(callback){ + /** + * @ngdoc method + * @name angular.service.$browser#notifyWhenNoOutstandingRequests + * @methodOf angular.service.$browser + * + * @param {function} callback Function that will be called when no outstanding request + */ + self.notifyWhenNoOutstandingRequests = function(callback) { if (outstandingRequestCount === 0) { callback(); } else { @@ -75,27 +95,48 @@ function Browser(location, document, head, XHR, $log) { // Poll Watcher API ////////////////////////////////////////////////////////////// var pollFns = []; - function poll(){ + + /** + * @ngdoc method + * @name angular.service.$browser#poll + * @methodOf angular.service.$browser + */ + self.poll = function() { foreach(pollFns, function(pollFn){ pollFn(); }); - } - self.poll = poll; + }; /** + * @ngdoc method + * @name angular.service.$browser#addPollFn + * @methodOf angular.service.$browser + * + * @param {function} fn Poll function to add + * + * @description * Adds a function to the list of functions that poller periodically executes - * @return {Function} the added function + * + * @returns {function} the added function */ - self.addPollFn = function(/**Function*/fn){ + self.addPollFn = function(fn) { pollFns.push(fn); return fn; }; /** - * Configures the poller to run in the specified intervals, using the specified setTimeout fn and - * kicks it off. + * @ngdoc method + * @name angular.service.$browser#startPoller + * @methodOf angular.service.$browser + * + * @param {number} interval How often should browser call poll functions (ms) + * @param {function} setTimeout + * + * @description + * Configures the poller to run in the specified intervals, using the specified + * setTimeout fn and kicks it off. */ - self.startPoller = function(/**number*/interval, /**Function*/setTimeout){ + self.startPoller = function(interval, setTimeout) { (function check(){ - poll(); + self.poll(); setTimeout(check, interval); })(); }; @@ -103,15 +144,37 @@ function Browser(location, document, head, XHR, $log) { ////////////////////////////////////////////////////////////// // URL API ////////////////////////////////////////////////////////////// + + /** + * @ngdoc method + * @name angular.service.$browser#setUrl + * @methodOf angular.service.$browser + * + * @param {string} url New url + * + * @description + * Sets browser's url + */ self.setUrl = function(url) { var existingURL = location.href; if (!existingURL.match(/#/)) existingURL += '#'; if (!url.match(/#/)) url += '#'; location.href = url; }; - self.getUrl = function() { + + /** + * @ngdoc method + * @name angular.service.$browser#getUrl + * @methodOf angular.service.$browser + * + * @description + * Get current browser's url + * + * @returns {string} Browser's url + */ + self.getUrl = function() { return location.href; - }; + }; ////////////////////////////////////////////////////////////// // Cookies API @@ -121,19 +184,27 @@ function Browser(location, document, head, XHR, $log) { var lastCookieString = ''; /** - * 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. + * @ngdoc method + * @name angular.service.$browser#cookies + * @methodOf angular.service.$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. * * The return values vary depending on the arguments that the method was called with as follows: - * <ul><li> - * cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify it - * </li><li> - * cookies(name, value) -> set name to value, if value is undefined delete the cookie - * </li><li> - * cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that way) - * </li></ul> + * <ul> + * <li>cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify it</li> + * <li>cookies(name, value) -> set name to value, if value is undefined delete the cookie</li> + * <li>cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that way)</li> + * </ul> + * + * @returns {Object} Hash of all cookies (if called without any parameter) */ - self.cookies = function (/**string*/name, /**string*/value){ + self.cookies = function (name, value) { var cookieLength, cookieArray, i, keyValue; if (name) { @@ -175,7 +246,27 @@ function Browser(location, document, head, XHR, $log) { // Misc API ////////////////////////////////////////////////////////////// var hoverListener = noop; + + /** + * @ngdoc method + * @name angular.service.$browser#hover + * @methodOf angular.service.$browser + * + * @param {function(Object, boolean)} listener + * + * @description + * Set hover listener - function that will be called when hover event occurs. + */ self.hover = function(listener) { hoverListener = listener; }; + + /** + * @ngdoc method + * @name angular.service.$browser#bind + * @methodOf angular.service.$browser + * + * @description + * Register hover function to real browser + */ self.bind = function() { document.bind("mouseover", function(event){ hoverListener(jqLite(msie ? event.srcElement : event.target), true); @@ -189,9 +280,15 @@ function Browser(location, document, head, XHR, $log) { /** + * @ngdoc method + * @name angular.service.$browser#addCss + * @methodOf angular.service.$browser + * + * @param {string} url Url to css file + * @description * Adds a stylesheet tag to the head. */ - self.addCss = function(/**string*/url) { + self.addCss = function(url) { var link = jqLite(rawDocument.createElement('link')); link.attr('rel', 'stylesheet'); link.attr('type', 'text/css'); @@ -201,9 +298,17 @@ function Browser(location, document, head, XHR, $log) { /** + * @ngdoc method + * @name angular.service.$browser#addJs + * @methodOf angular.service.$browser + * + * @param {string} url Url to js file + * @param {string=} dom_id Optional id for the script tag + * + * @description * Adds a script tag to the head. */ - self.addJs = function(/**string*/url, /**string*/dom_id) { + self.addJs = function(url, dom_id) { var script = jqLite(rawDocument.createElement('script')); script.attr('type', 'text/javascript'); script.attr('src', url); diff --git a/src/services.js b/src/services.js index 9af69403..be442c20 100644 --- a/src/services.js +++ b/src/services.js @@ -8,11 +8,64 @@ function angularServiceInject(name, fn, inject, eager) { angularService(name, fn, {$inject:inject, $creation:eager}); } +/** + * @ngdoc service + * @name angular.service.$window + * + * @description + * Is reference to the browser's <b>window</b> object. While <b>window</b> + * is globally available in JavaScript, it causes testability problems, because + * it is a global variable. In <b><angular/></b> we always refer to it through the + * $window service, so it may be overriden, removed or mocked for testing. + * + * All expressions are evaluated with respect to current scope so they don't + * suffer from window globality. + * + * @example + <input ng:init="greeting='Hello World!'" type="text" name="greeting" /> + <button ng:click="$window.alert(greeting)">ALERT</button> + */ angularServiceInject("$window", bind(window, identity, window), [], EAGER_PUBLISHED); + +/** + * @ngdoc service + * @name angular.service.$document + * @requires $window + * + * @description + * Reference to the browser window.document, but wrapped into angular.element(). + */ angularServiceInject("$document", function(window){ return jqLite(window.document); }, ['$window'], EAGER_PUBLISHED); +/** + * @ngdoc service + * @name angular.service.$location + * @requires $browser + * + * @property {string} href + * @property {string} protocol + * @property {string} host + * @property {number} port + * @property {string} path + * @property {Object.<string|boolean>} search + * @property {string} hash + * @property {string} hashPath + * @property {Object.<string|boolean>} hashSearch + * + * @description + * Parses the browser location url and makes it available to your application. + * Any changes to the url are reflected into $location service and changes to + * $location are reflected to url. + * Notice that using browser's forward/back buttons changes the $location. + * + * @example + <a href="#">clear hash</a> | + <a href="#myPath?name=misko">test hash</a><br/> + <input type='text' name="$location.hash"/> + <pre>$location = {{$location}}</pre> + */ angularServiceInject("$location", function(browser) { var scope = this, location = {toString:toString, update:update, updateHash: updateHash}, @@ -39,6 +92,11 @@ angularServiceInject("$location", function(browser) { // PUBLIC METHODS /** + * @ngdoc method + * @name angular.service.$location#update + * @methodOf angular.service.$location + * + * @description * Update location object * Does not immediately update the browser * Browser is updated at the end of $eval() @@ -69,7 +127,12 @@ angularServiceInject("$location", function(browser) { } /** - * Update location hash + * @ngdoc method + * @name angular.service.$location#updateHash + * @methodOf angular.service.$location + * + * @description + * Update location hash part * @see update() * * @example @@ -99,9 +162,12 @@ angularServiceInject("$location", function(browser) { } /** + * @ngdoc method + * @name angular.service.$location#toString + * @methodOf angular.service.$location + * + * @description * Returns string representation - href - * - * @return {string} Location's href property */ function toString() { updateLocation(); @@ -224,23 +290,105 @@ angularServiceInject("$location", function(browser) { }, ['$browser'], EAGER_PUBLISHED); +/** + * @ngdoc service + * @name angular.service.$log + * @requires $window + * + * @description + * Is simple service for logging. Default implementation writes the message + * into the browser's console (if present). + * + * This is useful for debugging. + * + * @example + <p>Reload this page with open console, enter text and hit the log button...</p> + Message: + <input type="text" name="message" value="Hello World!"/> + <button ng:click="$log.log(message)">log</button> + <button ng:click="$log.warn(message)">warn</button> + <button ng:click="$log.info(message)">info</button> + <button ng:click="$log.error(message)">error</button> + */ angularServiceInject("$log", function($window){ var console = $window.console || {log: noop, warn: noop, info: noop, error: noop}, log = console.log || noop; return { + /** + * @ngdoc method + * @name angular.service.$log#log + * @methodOf angular.service.$log + * + * @description + * Write a log message + */ log: bind(console, log), + + /** + * @ngdoc method + * @name angular.service.$log#warn + * @methodOf angular.service.$log + * + * @description + * Write a warning message + */ warn: bind(console, console.warn || log), + + /** + * @ngdoc method + * @name angular.service.$log#info + * @methodOf angular.service.$log + * + * @description + * Write an information message + */ info: bind(console, console.info || log), + + /** + * @ngdoc method + * @name angular.service.$log#error + * @methodOf angular.service.$log + * + * @description + * Write an error message + */ error: bind(console, console.error || log) }; }, ['$window'], EAGER_PUBLISHED); +/** + * @ngdoc service + * @name angular.service.$exceptionHandler + * @requires $log + * + * @description + * Any uncaught exception in <angular/> is delegated to this service. + * The default implementation simply delegates to $log.error which logs it into + * the browser console. + * + * When unit testing it is useful to have uncaught exceptions propagate + * to the test so the test will fail rather than silently log the exception + * to the browser console. For this purpose you can override this service with + * a simple rethrow. + * + * @example + */ angularServiceInject('$exceptionHandler', function($log){ return function(e) { $log.error(e); }; }, ['$log'], EAGER_PUBLISHED); +/** + * @ngdoc service + * @name angular.service.$hover + * @requires $browser + * @requires $document + * + * @description + * + * @example + */ angularServiceInject("$hover", function(browser, document) { var tooltip, self = this, error, width = 300, arrowWidth = 10, body = jqLite(document[0].body); browser.hover(function(element, show){ @@ -287,9 +435,15 @@ angularServiceInject("$hover", function(browser, document) { }); }, ['$browser', '$document'], EAGER); - -/* Keeps references to all invalid widgets found during validation. Can be queried to find if there - * are invalid widgets currently displayed +/** + * @ngdoc service + * @name angular.service.$invalidWidgets + * + * @description + * Keeps references to all invalid widgets found during validation. + * Can be queried to find whether there are any invalid widgets currently displayed. + * + * @example */ angularServiceInject("$invalidWidgets", function(){ var invalidWidgets = []; @@ -373,7 +527,59 @@ function switchRouteMatcher(on, when, dstName) { return match ? dst : _null; } -angularServiceInject('$route', function(location){ +/** + * @ngdoc service + * @name angular.service.$route + * @requires $location + * + * @property {Object} current Name of the current route + * @property {Array.<Object>} routes List of configured routes + * + * @description + * Watches $location.hashPath and tries to map the hash to an existing route + * definition. It is used for deep-linking URLs to controllers and views (HTML partials). + * + * $route is typically used in conjunction with ng:include widget. + * + * @example +<p> + This example shows how changing the URL hash causes the <tt>$route</tt> + to match a route against the URL, and the <tt>[[ng:include]]</tt> pulls in the partial. + Try changing the URL in the input box to see changes. +</p> + +<script> + angular.service('myApp', function($route) { + $route.when('/Book/:bookId', {template:'rsrc/book.html', controller:BookCntl}); + $route.when('/Book/:bookId/ch/:chapterId', {template:'rsrc/chapter.html', controller:ChapterCntl}); + $route.onChange(function() { + $route.current.scope.params = $route.current.params; + }); + }, {$inject: ['$route']}); + + function BookCntl() { + this.name = "BookCntl"; + } + + function ChapterCntl() { + this.name = "ChapterCntl"; + } +</script> + +Chose: +<a href="#/Book/Moby">Moby</a> | +<a href="#/Book/Moby/ch/1">Moby: Ch1</a> | +<a href="#/Book/Gatsby">Gatsby</a> | +<a href="#/Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a><br/> +<input type="text" name="$location.hashPath" size="80" /> +<pre>$location={{$location}}</pre> +<pre>$route.current.template={{$route.current.template}}</pre> +<pre>$route.current.params={{$route.current.params}}</pre> +<pre>$route.current.scope.name={{$route.current.scope.name}}</pre> +<hr/> +<ng:include src="$route.current.template" scope="$route.current.scope"/> + */ +angularServiceInject('$route', function(location) { var routes = {}, onChange = [], matcher = switchRouteMatcher, @@ -381,8 +587,32 @@ angularServiceInject('$route', function(location){ dirty = 0, $route = { routes: routes, + + /** + * @ngdoc method + * @name angular.service.$route#onChange + * @methodOf angular.service.$route + * + * @param {function} fn Function that will be called on route change + * + * @description + * Register a handler function that will be called when route changes + */ onChange: bind(onChange, onChange.push), - when:function (path, params){ + + /** + * @ngdoc method + * @name angular.service.$route#when + * @methodOf angular.service.$route + * + * @param {string} path Route path (matched against $location.hash) + * @param {Object} params + * @returns {Object} route object + * + * @description + * Add new route + */ + when:function (path, params) { if (angular.isUndefined(path)) return routes; var route = routes[path]; if (!route) route = routes[path] = {}; @@ -415,6 +645,17 @@ angularServiceInject('$route', function(location){ return $route; }, ['$location'], EAGER_PUBLISHED); +/** + * @ngdoc service + * @name angular.service.$xhr + * @requires $browser + * @requires $error + * @requires $log + * + * @description + * + * @example + */ angularServiceInject('$xhr', function($browser, $error, $log){ var self = this; return function(method, url, post, callback){ @@ -446,12 +687,32 @@ angularServiceInject('$xhr', function($browser, $error, $log){ }; }, ['$browser', '$xhr.error', '$log']); +/** + * @ngdoc service + * @name angular.service.$xhr.error + * @requires $log + * + * @description + * + * @example + */ angularServiceInject('$xhr.error', function($log){ return function(request, response){ $log.error('ERROR: XHR: ' + request.url, request, response); }; }, ['$log']); +/** + * @ngdoc service + * @name angular.service.$xhr.bulk + * @requires $xhr + * @requires $xhr.error + * @requires $log + * + * @description + * + * @example + */ angularServiceInject('$xhr.bulk', function($xhr, $error, $log){ var requests = [], scope = this; @@ -502,6 +763,15 @@ angularServiceInject('$xhr.bulk', function($xhr, $error, $log){ return bulkXHR; }, ['$xhr', '$xhr.error', '$log']); +/** + * @ngdoc service + * @name angular.service.$xhr.cache + * @requires $xhr + * + * @description + * + * @example + */ angularServiceInject('$xhr.cache', function($xhr){ var inflight = {}, self = this; function cache(method, url, post, callback, verifyCache){ @@ -546,18 +816,74 @@ angularServiceInject('$xhr.cache', function($xhr){ return cache; }, ['$xhr.bulk']); +/** + * @ngdoc service + * @name angular.service.$resource + * @requires $xhr + * + * @description + * Is a factory which creates a resource object which lets you interact with + * <a href="http://en.wikipedia.org/wiki/Representational_State_Transfer" target="_blank">RESTful</a> + * server-side data sources. + * Resource object has action methods which provide high-level behaviors without + * the need to interact with the low level $xhr or XMLHttpRequest(). + * + * @example + <script> + function BuzzController($resource) { + this.Activity = $resource( + 'https://www.googleapis.com/buzz/v1/activities/:userId/:visibility/:activityId/:comments', + {alt:'json', callback:'JSON_CALLBACK'}, + {get:{method:'JSON', params:{visibility:'@self'}}, replies: {method:'JSON', params:{visibility:'@self', comments:'@comments'}}} + ); + } + + BuzzController.prototype = { + fetch: function() { + this.activities = this.Activity.get({userId:this.userId}); + }, + expandReplies: function(activity) { + activity.replies = this.Activity.replies({userId:this.userId, activityId:activity.id}); + } + }; + BuzzController.$inject = ['$resource']; + </script> + + <div ng:controller="BuzzController"> + <input name="userId" value="googlebuzz"/> + <button ng:click="fetch()">fetch</button> + <hr/> + <div ng:repeat="item in activities.data.items"> + <h1 style="font-size: 15px;"> + <img src="{{item.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/> + <a href="{{item.actor.profileUrl}}">{{item.actor.name}}</a> + <a href="#" ng:click="expandReplies(item)" style="float: right;">Expand replies: {{item.links.replies[0].count}}</a> + </h1> + {{item.object.content | html}} + <div ng:repeat="reply in item.replies.data.items" style="margin-left: 20px;"> + <img src="{{reply.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/> + <a href="{{reply.actor.profileUrl}}">{{reply.actor.name}}</a>: {{reply.content | html}} + </div> + </div> + </div> + */ angularServiceInject('$resource', function($xhr){ var resource = new ResourceFactory($xhr); return bind(resource, resource.route); }, ['$xhr.cache']); - /** - * $cookies service provides read/write access to the browser cookies. Currently only session - * cookies are supported. - * - * Only a simple Object is exposed and by adding or removing properties to/from this object, new - * cookies are created or deleted from the browser at the end of the current eval. + * @ngdoc service + * @name angular.service.$cookies + * @requires $browser + * + * @description + * Provides read/write access to browser's cookies. + * + * Only a simple Object is exposed and by adding or removing properties to/from + * this object, new cookies are created/deleted at the end of current $eval. + * + * @example */ angularServiceInject('$cookies', function($browser) { var rootScope = this, @@ -630,23 +956,61 @@ angularServiceInject('$cookies', function($browser) { } }, ['$browser'], EAGER_PUBLISHED); - /** - * $cookieStore provides a key-value (string-object) storage that is backed by session cookies. - * Objects put or retrieved from this storage are automatically serialized or deserialized. + * @ngdoc service + * @name angular.service.$cookieStore + * @requires $cookies + * + * @description + * Provides a key-value (string-object) storage, that is backed by session cookies. + * Objects put or retrieved from this storage are automatically serialized or + * deserialized by angular's toJson/fromJson. + * @example */ angularServiceInject('$cookieStore', function($store) { return { + /** + * @ngdoc method + * @name angular.service.$cookieStore#get + * @methodOf angular.service.$cookieStore + * + * @description + * Returns the value of given cookie key + * + * @param {string} key + * @returns Cookie value + */ get: function(/**string*/key) { return fromJson($store[key]); }, - put: function(/**string*/key, /**Object*/value) { + /** + * @ngdoc method + * @name angular.service.$cookieStore#put + * @methodOf angular.service.$cookieStore + * + * @description + * Sets a value for given cookie key + * + * @param {string} key + * @param {Object} value + */ + put: function(key, value) { $store[key] = toJson(value); }, - remove: function(/**string*/key) { + /** + * @ngdoc method + * @name angular.service.$cookieStore#remove + * @methodOf angular.service.$cookieStore + * + * @description + * Remove given cookie + * + * @param {string} key + */ + remove: function(key) { delete $store[key]; } }; |
