├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bower.json ├── example ├── README.md ├── app │ └── views │ │ └── index.ejs ├── package.json ├── public │ ├── components │ │ ├── Countable │ │ │ ├── .bower.json │ │ │ ├── Countable.js │ │ │ ├── LICENSE.md │ │ │ ├── README.md │ │ │ └── bower.json │ │ ├── angular │ │ │ ├── .bower.json │ │ │ ├── README.md │ │ │ ├── angular-csp.css │ │ │ ├── angular.js │ │ │ ├── angular.min.js │ │ │ ├── angular.min.js.gzip │ │ │ ├── angular.min.js.map │ │ │ ├── bower.json │ │ │ └── package.json │ │ ├── pusher-angular │ │ │ ├── .bower.json │ │ │ ├── bower.json │ │ │ ├── example │ │ │ │ ├── app │ │ │ │ │ └── views │ │ │ │ │ │ └── index.ejs │ │ │ │ ├── package.json │ │ │ │ ├── public │ │ │ │ │ ├── components │ │ │ │ │ │ ├── Countable │ │ │ │ │ │ │ ├── Countable.js │ │ │ │ │ │ │ ├── LICENSE.md │ │ │ │ │ │ │ └── bower.json │ │ │ │ │ │ ├── angular │ │ │ │ │ │ │ ├── angular-csp.css │ │ │ │ │ │ │ ├── angular.js │ │ │ │ │ │ │ ├── angular.min.js │ │ │ │ │ │ │ ├── angular.min.js.gzip │ │ │ │ │ │ │ ├── angular.min.js.map │ │ │ │ │ │ │ ├── bower.json │ │ │ │ │ │ │ └── package.json │ │ │ │ │ │ ├── pusher-angular │ │ │ │ │ │ │ ├── bower.json │ │ │ │ │ │ │ └── lib │ │ │ │ │ │ │ │ └── pusher-angular.js │ │ │ │ │ │ └── ui-router │ │ │ │ │ │ │ ├── CHANGELOG.md │ │ │ │ │ │ │ ├── api │ │ │ │ │ │ │ └── angular-ui-router.d.ts │ │ │ │ │ │ │ ├── bower.json │ │ │ │ │ │ │ ├── release │ │ │ │ │ │ │ ├── angular-ui-router.js │ │ │ │ │ │ │ └── angular-ui-router.min.js │ │ │ │ │ │ │ └── src │ │ │ │ │ │ │ ├── common.js │ │ │ │ │ │ │ ├── resolve.js │ │ │ │ │ │ │ ├── state.js │ │ │ │ │ │ │ ├── stateDirectives.js │ │ │ │ │ │ │ ├── stateFilters.js │ │ │ │ │ │ │ ├── templateFactory.js │ │ │ │ │ │ │ ├── urlMatcherFactory.js │ │ │ │ │ │ │ ├── urlRouter.js │ │ │ │ │ │ │ ├── view.js │ │ │ │ │ │ │ ├── viewDirective.js │ │ │ │ │ │ │ └── viewScroll.js │ │ │ │ │ ├── css │ │ │ │ │ │ └── style.css │ │ │ │ │ ├── js │ │ │ │ │ │ ├── app.js │ │ │ │ │ │ ├── controllers.js │ │ │ │ │ │ ├── directives.js │ │ │ │ │ │ └── services.js │ │ │ │ │ └── views │ │ │ │ │ │ ├── setup.html │ │ │ │ │ │ └── text.html │ │ │ │ └── server.js │ │ │ ├── lib │ │ │ │ ├── pusher-angular.js │ │ │ │ └── pusher-angular.min.js │ │ │ ├── package.json │ │ │ ├── public │ │ │ │ └── components │ │ │ │ │ ├── angular-mocks │ │ │ │ │ ├── angular-mocks.js │ │ │ │ │ ├── bower.json │ │ │ │ │ ├── ngAnimateMock.js │ │ │ │ │ ├── ngMock.js │ │ │ │ │ ├── ngMockE2E.js │ │ │ │ │ └── package.json │ │ │ │ │ └── angular │ │ │ │ │ ├── angular-csp.css │ │ │ │ │ ├── angular.js │ │ │ │ │ ├── angular.min.js │ │ │ │ │ ├── angular.min.js.gzip │ │ │ │ │ ├── angular.min.js.map │ │ │ │ │ ├── bower.json │ │ │ │ │ ├── index.js │ │ │ │ │ └── package.json │ │ │ └── travis_karma.conf.js │ │ └── ui-router │ │ │ ├── .bower.json │ │ │ ├── CHANGELOG.md │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── api │ │ │ └── angular-ui-router.d.ts │ │ │ ├── bower.json │ │ │ ├── release │ │ │ ├── angular-ui-router.js │ │ │ └── angular-ui-router.min.js │ │ │ └── src │ │ │ ├── common.js │ │ │ ├── resolve.js │ │ │ ├── state.js │ │ │ ├── stateDirectives.js │ │ │ ├── stateFilters.js │ │ │ ├── templateFactory.js │ │ │ ├── urlMatcherFactory.js │ │ │ ├── urlRouter.js │ │ │ ├── view.js │ │ │ ├── viewDirective.js │ │ │ └── viewScroll.js │ ├── css │ │ └── style.css │ ├── js │ │ ├── app.js │ │ ├── controllers.js │ │ ├── directives.js │ │ └── services.js │ └── views │ │ ├── setup.html │ │ └── text.html └── server.js ├── karma.conf.js ├── lib ├── pusher-angular.js └── pusher-angular.min.js ├── package.json ├── public └── components │ ├── angular-mocks │ ├── .bower.json │ ├── README.md │ ├── angular-mocks.js │ ├── bower.json │ ├── ngAnimateMock.js │ ├── ngMock.js │ ├── ngMockE2E.js │ └── package.json │ └── angular │ ├── .bower.json │ ├── README.md │ ├── angular-csp.css │ ├── angular.js │ ├── angular.min.js │ ├── angular.min.js.gzip │ ├── angular.min.js.map │ ├── bower.json │ ├── index.js │ └── package.json ├── tests └── specs.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | bower_components/ 3 | node_modules/ 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "node" 4 | before_install: 5 | - "export DISPLAY=:99.0" 6 | - "sh -e /etc/init.d/xvfb start" 7 | - sleep 3 8 | install: 9 | - yarn install 10 | script: 11 | - karma start karma.conf.js 12 | after_script: 13 | - cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js 14 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 1.0.0 4 | 5 | * Support for pusher-js 4.x.x added (@anthwinter) 6 | 7 | ## 0.1.9 8 | 9 | * Add option to skip calling $digest (@robwalkerco) 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2014 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This library is no longer supported 2 | 3 | 4 | 5 | # Pusher Channels AngularJS Library 6 | 7 | [![Build Status](https://travis-ci.org/pusher/pusher-angular.svg?branch=master)](https://travis-ci.org/pusher/pusher-angular) 8 | [![Coverage Status](https://img.shields.io/coveralls/pusher/pusher-angular.svg)](https://coveralls.io/r/pusher/pusher-angular?branch=master) 9 | 10 | This library is an open source client that allows you to connect to [Pusher Channels](http://pusher.com/channels). It keeps largely the same API as the [pusher-js library](http://github.com/pusher/pusher-js/), with a few differences. 11 | 12 | Currently only AngularJS (version 1.x) is supported. 13 | 14 | ## Usage overview 15 | 16 | The following topics are covered: 17 | 18 | * Initialisation 19 | * Subscribing to channels (public, private, encrypted and presence) 20 | * Accessing Channels 21 | * Binding to events 22 | * Globally 23 | * Per-channel 24 | * Bind to everything 25 | * Presence channel members 26 | * Connection 27 | 28 | 29 | ## Initialisation 30 | 31 | The first step is to make sure that you have all of the required libraries available to your app before you begin. You'll need something like this in your `index.html` (or similar): 32 | 33 | ````html 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 46 | ```` 47 | 48 | If you'd like you can use Bower to install pusher-angular using the following command: 49 | 50 | ````bash 51 | bower install pusher-angular --save 52 | ```` 53 | 54 | With that in place, to start using the AngularJS library you first need to create a `Pusher` client in exactly the same way that you create one using the [pusher-js library](http://github.com/pusher/pusher-js/), which is as follows: 55 | 56 | ````javascript 57 | var pusher = new Pusher(API_KEY); 58 | ```` 59 | 60 | There are a number of configuration parameters which can be set for the `Pusher` client, which can be passed as an object to the constructor, i.e.: 61 | 62 | ````javascript 63 | var pusher = new Pusher(API_KEY, { 64 | authEndpoint: "http://example.com/pusher/auth" 65 | }); 66 | ```` 67 | 68 | This is all documented in full [here](http://github.com/pusher/pusher-js/). 69 | 70 | When you've created a `Pusher` client you then need to pass that client to a `$pusher` object inside your AngularJS controller, service, etc: 71 | 72 | ````javascript 73 | angular.module('myApp').controller('MyController', ['$scope', '$pusher', 74 | function($scope, $pusher) { 75 | var client = new Pusher(API_KEY); 76 | var pusher = $pusher(client); 77 | }]); 78 | ```` 79 | 80 | You can also see here that you need to inject the `$pusher` service into any controllers, services, etc where you'd like to use Channels in an AngularJS context. 81 | 82 | To make the `$pusher` service available to be used throughout your app you need to ensure that the `pusher-angular` module is included in your app. You do this by having the following in your app: 83 | 84 | ````javascript 85 | angular.module('myApp', ['pusher-angular']) 86 | ```` 87 | 88 | Note that you can choose to define just one `Pusher` client, should you prefer, and then use that as the client throughout your AngularJS app. You can do this by simply instantiating a client as follows: 89 | 90 | ````javascript 91 | window.client = new Pusher('API_KEY'); 92 | ```` 93 | 94 | and then instantiating instances of `$pusher` in your AngularJS app using the standard: 95 | 96 | ````javascript 97 | var pusher = $pusher(client); 98 | ```` 99 | 100 | Make sure that you define client before then referencing it in your AngularJS app though. 101 | 102 | This is all of the setup required to have Channels available in your AngularJS app. The content below will explain how you can utilise Channels in an AngularJS app. 103 | 104 | ## Subscribing to channels 105 | 106 | ### Public channels 107 | 108 | The default method for subscribing to a channel involves invoking the `subscribe` method of your `$pusher` object (named `pusher` throughout the examples provided here): 109 | 110 | ````javascript 111 | var my_channel = pusher.subscribe('my-channel'); 112 | ```` 113 | 114 | This returns a Channel object which events can be bound to. 115 | 116 | ### Private channels 117 | 118 | Private channels are created in exactly the same way as normal channels, except that they reside in the 'private-' namespace. This means prefixing the channel name: 119 | 120 | ````javascript 121 | var my_private_channel = pusher.subscribe('private-my-channel'); 122 | ```` 123 | 124 | ### Presence channels 125 | 126 | Presence channels are again created in exactly the same way as normal channels, except that they reside in the 'presence-' namespace. This means prefixing the channel name: 127 | 128 | ````javascript 129 | var my_presence_channel = pusher.subscribe('presence-my-channel'); 130 | ```` 131 | 132 | It is possible to access channels by name, through the `channel` function: 133 | 134 | ````javascript 135 | channel = pusher.channel('private-my-channel'); 136 | ```` 137 | 138 | It is possible to access all subscribed channels through the `allChannels` function: 139 | 140 | ````javascript 141 | var channels = pusher.allChannels(); 142 | console.group('Channels - subscribed to:'); 143 | for (var i = 0; i < channels.length; i++) { 144 | var channel = channels[i]; 145 | console.log(channel.name); 146 | } 147 | console.groupEnd(); 148 | ```` 149 | 150 | ### Encrypted Channels (BETA) 151 | 152 | Like private channels, encrypted channels have their own namespace, 'private-encrypted-'. For more information about encrypted channels, please see the [docs](https://pusher.com/docs/client_api_guide/client_encrypted_channels). 153 | 154 | ```js 155 | var my_private_encrypted_channel = pusher.subscribe('private-encrypted-my-channel'); 156 | ``` 157 | 158 | ## Accessing Channels 159 | 160 | It is possible to access channels by name, through the `channel` function: 161 | 162 | ```js 163 | var my_private_encrypted_channel = pusher.channel('private-my-channel'); 164 | ``` 165 | 166 | It is possible to access all subscribed channels through the `allChannels` function: 167 | 168 | ```js 169 | pusher.allChannels().forEach(channel => console.log(channel.name)); 170 | ``` 171 | 172 | Private, presence and encrypted channels will make a request to your `authEndpoint` (`/pusher/auth`) by default, where you will have to [authenticate the subscription](https://pusher.com/docs/authenticating_users). You will have to send back the correct auth response and a 200 status code. 173 | 174 | ## Binding to events 175 | 176 | Events can be bound to at 2 levels, the global, and per channel. They take a very similar form to the way events are handled in jQuery. Note that this is one area in which the API differs to pusher-js. In pusher-angular, a call to `bind` will return a decorated version of the callback / handler that you pass as a parameter. You will need to assign this to a variable if you wish to unbind the handler from the object in the future. This is explained in the docs for unbinding below. 177 | 178 | ### Global events 179 | 180 | You can attach behaviour to these events regardless of the channel the event is broadcast to. The following is an example of an app that binds to new comments from any channel: 181 | 182 | ````javascript 183 | var client = new Pusher(API_KEY); 184 | var pusher = $pusher(client); 185 | pusher.subscribe('my-channel'); 186 | pusher.bind('new-comment', 187 | function(data) { 188 | // add comment into page 189 | } 190 | ); 191 | ```` 192 | 193 | ### Per-channel events 194 | 195 | These are bound to a specific channel, and mean that you can reuse event names in different parts of your client application. The following might be an example of a stock tracking app where several channels are opened for different companies: 196 | 197 | ````javascript 198 | var client = new Pusher(API_KEY); 199 | var pusher = $pusher(client); 200 | var my_channel = pusher.subscribe('my-channel'); 201 | my_channel.bind('new-price', 202 | function(data) { 203 | // update with new price 204 | } 205 | ); 206 | ```` 207 | 208 | ### Binding to everything 209 | 210 | It is possible to bind to all events at either the global or channel level by using the method `bind_all`. This is used for debugging, but may have other utilities. 211 | 212 | ### Unbind event handlers 213 | 214 | Remove previously-bound handlers from an object. Only handlers that match all of the provided arguments (`eventName`, `handler` or `context`) are removed. 215 | 216 | ````javascript 217 | var handler = function() { console.log('testing'); }; 218 | var decoratedHandler = my_channel.bind('new-comment', handler); 219 | 220 | channel.unbind('new-comment', decoratedHandler); // removes just `decoratedHandler` for the `new-comment` event 221 | channel.unbind('new-comment'); // removes all handlers for the `new-comment` event 222 | channel.unbind(null, decoratedHandler); // removes `decoratedHandler` for all events 223 | channel.unbind(null, null, context); // removes all handlers for `context` 224 | channel.unbind(); // removes all handlers on `channel` 225 | ```` 226 | 227 | The same API applies to unbinding handlers from the client object. 228 | 229 | ## Presence channel members 230 | 231 | All presence channels have a members object that contains information about all of the members in the channel. More specific information can be found in the [Channels docs](https://pusher.com/docs/channels/using_channels/presence-channels#channel_members). 232 | 233 | In this library the `members` object is setup to automatically reflect changes in members of the channel. That means if you had the following code in a controller: 234 | 235 | ````javascript 236 | angular.module('myApp').controller('MyController', ['$scope', '$pusher', 237 | function($scope, $pusher) { 238 | var client = new Pusher(API_KEY); 239 | var pusher = $pusher(client); 240 | 241 | var presence = pusher.subscribe('presence-test'); 242 | $scope.members = presence.members; 243 | }]); 244 | ```` 245 | 246 | and the following HTML in your view: 247 | 248 | ````html 249 |
250 | {{members.count}} 251 | {{members.members}} 252 |
253 | ```` 254 | 255 | your view would update the members count and the members list whenever there was a member added or removed from the channel. 256 | 257 | 258 | ## Connection 259 | 260 | You can bind to specific events on your `$pusher` objects connection, such as `state_change`, using the following code: 261 | 262 | ````javascript 263 | var client = new Pusher(API_KEY); 264 | var pusher = $pusher(client); 265 | 266 | pusher.connection.bind('state_change', function (states) { 267 | // var previous = states.previous ... 268 | }) 269 | ```` 270 | 271 | Similarly to the client and channel, you can also bind to all events on a connection using the `bind_all` method. That looks like this: 272 | 273 | ````javascript 274 | var client = new Pusher(API_KEY); 275 | var pusher = $pusher(client); 276 | 277 | pusher.connection.bind_all(function (eventName, data) { 278 | // if (eventName == 'state_change') { ... 279 | }) 280 | ```` 281 | 282 | 283 | ## Contributing 284 | 285 | If you'd like to contribute to the library then fork it, hack away at it, improve it, test it, and then make a pull request. 286 | 287 | You can make sure that your changes / improvements don't break anything by running the unit tests. To run them just run `karma start` and then you can go ahead and make changes to the library and the tests and watch the tests run again to see if they're still passing. 288 | 289 | You can generate the minimized file as following: 290 | 291 | ```bash 292 | uglifyjs lib/pusher-angular.js -m -o lib/pusher-angular.min.js 293 | ``` 294 | 295 | ## Support 296 | 297 | If you have questions that aren't answered here or in the code's inline documentation then feel free to create an issue describing your problem. 298 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pusher-angular", 3 | "main": "lib/pusher-angular.js", 4 | "version": "1.0.0", 5 | "homepage": "https://github.com/pusher/pusher-angular", 6 | "authors": [ 7 | "Hamilton Chapman " 8 | ], 9 | "description": "The official Pusher Angular library", 10 | "keywords": [ 11 | "angular", 12 | "angularjs", 13 | "pusher", 14 | "realtime", 15 | "websocket" 16 | ], 17 | "license": "MIT", 18 | "ignore": [ 19 | "**/.*", 20 | "tests", 21 | "karma.conf.js", 22 | "README.md", 23 | "LICENSE", 24 | "example" 25 | ], 26 | "devDependencies": { 27 | "angular-mocks": "latest" 28 | }, 29 | "dependencies": { 30 | "angular": "latest" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Pusher Angular Library Example App 2 | 3 | ## Setup 4 | 5 | All you need to do to get up and running is to `npm install`, and then put your Pusher app id, key and secret into `server.js` and in `js/services.js`. -------------------------------------------------------------------------------- /example/app/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Pusher Collaboration 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pusher-angular", 3 | "version": "0.1.0", 4 | "description": "An example Angular app using pusher-angular library", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/pusher/pusher-angular.git" 8 | }, 9 | "author": "Hamilton Chapman", 10 | "license": "MIT", 11 | "bugs": { 12 | "url": "https://github.com/pusher/pusher-angular/issues" 13 | }, 14 | "homepage": "http://github.com/pusher/pusher-angular", 15 | "engines": { 16 | "node": "0.10.26" 17 | }, 18 | "dependencies": { 19 | "body-parser": "^1.9.0", 20 | "ejs": "^1.0.0", 21 | "express": "^4.9.5", 22 | "pusher": "^1.0.1" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /example/public/components/Countable/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Countable", 3 | "description": "Countable is a JavaScript function to add live paragraph-, word- and character-counting to an HTML element.", 4 | "version": "2.0.2", 5 | "main": "Countable.js", 6 | "ignore": [ 7 | "test", 8 | "CONTRIBUTING.md", 9 | "CHANGELOG.md", 10 | "karma.conf.js", 11 | "package.json", 12 | ".travis.yml" 13 | ], 14 | "devDependencies": { 15 | "chai": "~1.7.2" 16 | }, 17 | "homepage": "https://github.com/RadLikeWhoa/Countable", 18 | "_release": "2.0.2", 19 | "_resolution": { 20 | "type": "version", 21 | "tag": "v2.0.2", 22 | "commit": "056edffccf09a1e4d070392ef5f59afc97e93518" 23 | }, 24 | "_source": "git://github.com/RadLikeWhoa/Countable.git", 25 | "_target": "~2.0.2", 26 | "_originalSource": "Countable", 27 | "_direct": true 28 | } -------------------------------------------------------------------------------- /example/public/components/Countable/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (C) 2014 Sacha Schmid 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /example/public/components/Countable/README.md: -------------------------------------------------------------------------------- 1 | # Countable 2 | 3 | [![Build Status](https://travis-ci.org/RadLikeWhoa/Countable.png?branch=master)](https://travis-ci.org/RadLikeWhoa/Countable) 4 | 5 | Countable is a JavaScript function to add **live paragraph-, word- and character-counting** to an HTML element. Countable is a *zero-dependency* library and comes in at **1KB** when minified and gzipped. 6 | 7 | [**View the Demo**](http://radlikewhoa.github.io/Countable#demo) 8 | 9 | ## Installation 10 | 11 | The preferred method of installation is [**bower**](https://github.com/bower/bower). 12 | 13 | ``` 14 | bower install Countable 15 | ``` 16 | 17 | Alternatively, you can download the latest [zipball](https://github.com/RadLikeWhoa/Countable/archive/master.zip) or copy the [script](https://raw.github.com/RadLikeWhoa/Countable/master/Countable.js) directly. 18 | 19 | ## Usage 20 | 21 | Countable is available as a Node / CommonJS module, an AMD module and as a global. All methods are accessed on the Countable object directly. 22 | 23 | ### Callbacks 24 | 25 | The `live` and `once` methods both accept a callback. The given callback is then called whenever needed with a single parameter that carries all the releavant data. `this` is bound to the current element. Take the following code for an example. 26 | 27 | ```javascript 28 | var area = document.getElementById('text') 29 | 30 | Countable.once(area, function (counter) { 31 | console.log(this, counter) 32 | }) 33 | ``` 34 | 35 | ``` 36 | => , { all: 0, characters: 0, paragraphs: 0, words: 0 } 37 | ``` 38 | 39 | Property | Meaning 40 | ---------- | -------------------------------------------------------------------------------------------- 41 | paragraphs | The number of paragraphs. Paragraphs can be separated by either a soft or a hard (two line breaks) return. To use hard returns, set the corresponding option (`hardReturns`). 42 | words | The number of words. Words are split using spaces. 43 | characters | The number of characters (without spaces). This contains all non-whitespace characters. 44 | all | The number of characters including whitespace. This is the total number of all characters in the element. 45 | 46 | ### Countable#live(elements, callback, options) 47 | 48 | Bind the callback to all given elements. The callback gets called everytime the element's value or text is changed. 49 | 50 | ```javascript 51 | Countable.live(area, function (counter) { 52 | console.log(counter) 53 | }) 54 | ``` 55 | 56 | ### Countable#die(elements) 57 | 58 | Remove the bound callback from all given elements. 59 | 60 | ```javascript 61 | Countable.die(area) 62 | ``` 63 | 64 | ### Countable#once(elements, callback, options) 65 | 66 | Similar to `Countable.live()`, but the callback is only executed once, there are no events bound. 67 | 68 | ```javascript 69 | Countable.once(area, function (counter) { 70 | console.log(counter) 71 | }) 72 | ``` 73 | 74 | ### Countable#enabled(element) 75 | 76 | Checks the live-counting functionality is bound to the given. 77 | 78 | ```javascript 79 | Countable.enabled(area) 80 | ``` 81 | 82 | ### Options 83 | 84 | `Countable.live()` and `Countable.once()` both accept a third argument, an options object that allows you to change how Countable treats certain aspects of your element's text. 85 | 86 | ```javascript 87 | { 88 | hardReturns: false, 89 | stripTags: false, 90 | ignoreReturns: false 91 | } 92 | ``` 93 | 94 | By default, paragraphs are split by a single return (a soft return). By setting `hardReturns` to true, Countable splits paragraphs after two returns. 95 | 96 | Depending on your application and audience, you might need to strip HTML tags from the text before counting it. You can do this by setting `stripTags` to true. 97 | 98 | In most cases, returns should be counted as part of the `all` property. Set `ignoreReturns` to false to remove them from the counter. 99 | 100 | ## Browser Support 101 | 102 | Countable supports all modern browsers. Internet Explorer is supported down to version 7. Note that some browsers don't implement the `oninput` event consistently so there might be differences in the way Countable works in different browsers. 103 | 104 | ## Upgrading from version 1.x.x 105 | 106 | Upgrading from version 1.x.x is easy. Most likely, you've used something like the following: 107 | 108 | ```javascript 109 | var area = document.getElementById('area') 110 | 111 | new Countable(area, function (counter) { 112 | console.log(counter) 113 | }, { stripTags: true }) 114 | ``` 115 | 116 | The new syntax offers more functions as described above, but to keep the live-counting functionality, you just write this: 117 | 118 | ```javascript 119 | var area = document.getElementById('area') 120 | 121 | Countable.live(area, function (counter) { 122 | console.log(counter) 123 | }, { stripTags: true }) 124 | ``` 125 | 126 | * The callback parameter is no longer optional 127 | * `options.once` has been replaced with `Countable.once()` 128 | * `Countable.live()` and `Countable.once()` both accept one or more elements, rather than just a single one 129 | * Inside the callback, `this` is now bound to the current element 130 | 131 | ## Changelog 132 | 133 | ### 2.0.2 _(2014-02-19)_ 134 | 135 | * NEW: Returns are counted as part of the `all` property. A new option `ignoreReturns` was added to restore the old behaviour. 136 | 137 | ### 2.0.1 _(2013-07-13)_ 138 | 139 | * FIX: Missing parameter in `Countable.once`. (Thanks to [MrOPR](https://github.com/RadLikeWhoa/Countable/pull/18)) 140 | 141 | ### 2.0.0 _(2013-05-25)_ 142 | 143 | * NEW: Countable has a new Syntax. You can now use `Countable.live`, `Countable.once`, `Countable.die` and `Countable.enabled`. Notes on upgrading is provided in the README. 144 | * NEW: Countable can now work on multiple elements with one function call. 145 | * FIX: Prevent a XSS bug. (Thanks to [Rob--W](https://github.com/RadLikeWhoa/Countable/pull/17)) 146 | 147 | ### 1.4.2 _(2013-05-23)_ 148 | 149 | * FIX: Fix a bug where options wouldn't be applied correctly. 150 | 151 | ### 1.4.1 _(2013-05-22)_ 152 | 153 | * NEW: Added option to execute the callback only once. 154 | 155 | ### 1.4.0 _(2013-05-20)_ 156 | 157 | * NEW: Allow for an options object as the third parameter. 158 | 159 | ### 1.3.0 _(2013-05-16)_ 160 | 161 | * NEW: Countable is now available as an AMD and CommonJS module. 162 | * FIX: Better handle `textarea` with predefined value. (Thanks to [besmithett](https://github.com/RadLikeWhoa/Countable/pull/15)) 163 | 164 | ### 1.2.0 _(2013-05-02)_ 165 | 166 | * NEW: Optionally strip HTML tags. (Thanks to [craniumslows](https://github.com/RadLikeWhoa/Countable/pull/13)) 167 | * NEW: Include ucs2decode function from the [punycode](https://github.com/bestiejs/punycode.js) library to better handle special characters. (Thanks to [craniumslows](https://github.com/RadLikeWhoa/Countable/pull/13)) 168 | * IMPROVED: Better handling of punctuation. 169 | 170 | ### 1.1.1 _(2013-03-16)_ 171 | 172 | * IMPROVED: Better support for foreign languages and special characters. 173 | 174 | ### 1.1.0 _(2013-03-12)_ 175 | 176 | * NEW: Include number of characters including whitespace. 177 | * NEW: Countable is now available on Bower. 178 | * IMPROVED: Improve performance when counting the values. 179 | * IMPROVED: Improve performance when trimming strings by using `String::trim` when available. 180 | * IMPROVED: Better documentation. 181 | 182 | ### 1.0.0 _(2013-03-11)_ 183 | 184 | * Initial release 185 | 186 | ## About the Author 187 | 188 | My name is [Sacha Schmid](http://sachaschmid.ch) ([**@sachaschmid**](https://twitter.com/sachaschmid)). I'm a front-end engineer from Switzerland. I am the creator of [SSGS](http://github.com/RadLikeWhoa/SSGS) and [other open source projects](https://github.com/RadLikeWhoa). 189 | 190 | Are you using Countable in a project? I'd love to see what you've achieved. Just [**send me a tweet**](https://twitter.com/sachaschmid). 191 | 192 | ### Contributors 193 | 194 | * [@epmatsw](https://github.com/epmatsw) 195 | * [@craniumslows](https://github.com/craniumslows) 196 | * [@Rob--W](https://github.com/Rob--W) 197 | -------------------------------------------------------------------------------- /example/public/components/Countable/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Countable", 3 | "description": "Countable is a JavaScript function to add live paragraph-, word- and character-counting to an HTML element.", 4 | "version": "2.0.2", 5 | "main": "Countable.js", 6 | "ignore": [ 7 | "test", 8 | "CONTRIBUTING.md", 9 | "CHANGELOG.md", 10 | "karma.conf.js", 11 | "package.json", 12 | ".travis.yml" 13 | ], 14 | "devDependencies": { 15 | "chai": "~1.7.2" 16 | } 17 | } -------------------------------------------------------------------------------- /example/public/components/angular/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular", 3 | "version": "1.2.26", 4 | "main": "./angular.js", 5 | "dependencies": {}, 6 | "homepage": "https://github.com/angular/bower-angular", 7 | "_release": "1.2.26", 8 | "_resolution": { 9 | "type": "version", 10 | "tag": "v1.2.26", 11 | "commit": "7308d8d650b2b9948796035cbf6f3b175d45efe0" 12 | }, 13 | "_source": "git://github.com/angular/bower-angular.git", 14 | "_target": "~1.2.26", 15 | "_originalSource": "angular", 16 | "_direct": true 17 | } -------------------------------------------------------------------------------- /example/public/components/angular/README.md: -------------------------------------------------------------------------------- 1 | # bower-angular 2 | 3 | This repo is for distribution on `bower`. The source for this module is in the 4 | [main AngularJS repo](https://github.com/angular/angular.js). 5 | Please file issues and pull requests against that repo. 6 | 7 | ## Install 8 | 9 | Install with `bower`: 10 | 11 | ```shell 12 | bower install angular 13 | ``` 14 | 15 | Add a ` 19 | ``` 20 | 21 | ## Documentation 22 | 23 | Documentation is available on the 24 | [AngularJS docs site](http://docs.angularjs.org/). 25 | 26 | ## License 27 | 28 | The MIT License 29 | 30 | Copyright (c) 2010-2012 Google, Inc. http://angularjs.org 31 | 32 | Permission is hereby granted, free of charge, to any person obtaining a copy 33 | of this software and associated documentation files (the "Software"), to deal 34 | in the Software without restriction, including without limitation the rights 35 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 36 | copies of the Software, and to permit persons to whom the Software is 37 | furnished to do so, subject to the following conditions: 38 | 39 | The above copyright notice and this permission notice shall be included in 40 | all copies or substantial portions of the Software. 41 | 42 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 43 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 44 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 45 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 46 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 47 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 48 | THE SOFTWARE. 49 | -------------------------------------------------------------------------------- /example/public/components/angular/angular-csp.css: -------------------------------------------------------------------------------- 1 | /* Include this file in your html if you are using the CSP mode. */ 2 | 3 | @charset "UTF-8"; 4 | 5 | [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], 6 | .ng-cloak, .x-ng-cloak, 7 | .ng-hide { 8 | display: none !important; 9 | } 10 | 11 | ng\:form { 12 | display: block; 13 | } 14 | 15 | .ng-animate-block-transitions { 16 | transition:0s all!important; 17 | -webkit-transition:0s all!important; 18 | } 19 | 20 | /* show the element during a show/hide animation when the 21 | * animation is ongoing, but the .ng-hide class is active */ 22 | .ng-hide-add-active, .ng-hide-remove { 23 | display: block!important; 24 | } 25 | -------------------------------------------------------------------------------- /example/public/components/angular/angular.min.js.gzip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pusher/pusher-angular/26f48e0ed23e40f63cb8faa8b7a8bc70a716d95a/example/public/components/angular/angular.min.js.gzip -------------------------------------------------------------------------------- /example/public/components/angular/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular", 3 | "version": "1.2.26", 4 | "main": "./angular.js", 5 | "dependencies": { 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /example/public/components/angular/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular", 3 | "version": "", 4 | "description": "HTML enhanced for web apps", 5 | "main": "angular.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/angular/angular.js.git" 12 | }, 13 | "keywords": [ 14 | "angular", 15 | "framework", 16 | "browser", 17 | "client-side" 18 | ], 19 | "author": "Angular Core Team ", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/angular/angular.js/issues" 23 | }, 24 | "homepage": "http://angularjs.org" 25 | } 26 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pusher-angular", 3 | "main": "lib/pusher-angular.js", 4 | "version": "0.1.7", 5 | "homepage": "https://github.com/pusher/pusher-angular", 6 | "authors": [ 7 | "Hamilton Chapman " 8 | ], 9 | "description": "The official Pusher Angular library", 10 | "keywords": [ 11 | "angular", 12 | "angularjs", 13 | "pusher", 14 | "realtime", 15 | "websocket" 16 | ], 17 | "license": "MIT", 18 | "ignore": [ 19 | "**/.*", 20 | "tests", 21 | "karma.conf.js", 22 | "README.md", 23 | "LICENSE" 24 | ], 25 | "devDependencies": { 26 | "angular-mocks": "latest" 27 | }, 28 | "dependencies": { 29 | "angular": "latest" 30 | }, 31 | "_release": "0.1.7", 32 | "_resolution": { 33 | "type": "version", 34 | "tag": "v0.1.7", 35 | "commit": "49a4edd0d8260d2727a0317ae460f99ef3f71a8a" 36 | }, 37 | "_source": "git://github.com/pusher/pusher-angular.git", 38 | "_target": "~0.1.7", 39 | "_originalSource": "pusher-angular", 40 | "_direct": true 41 | } -------------------------------------------------------------------------------- /example/public/components/pusher-angular/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pusher-angular", 3 | "main": "lib/pusher-angular.js", 4 | "version": "0.1.7", 5 | "homepage": "https://github.com/pusher/pusher-angular", 6 | "authors": [ 7 | "Hamilton Chapman " 8 | ], 9 | "description": "The official Pusher Angular library", 10 | "keywords": [ 11 | "angular", 12 | "angularjs", 13 | "pusher", 14 | "realtime", 15 | "websocket" 16 | ], 17 | "license": "MIT", 18 | "ignore": [ 19 | "**/.*", 20 | "tests", 21 | "karma.conf.js", 22 | "README.md", 23 | "LICENSE" 24 | ], 25 | "devDependencies": { 26 | "angular-mocks": "latest" 27 | }, 28 | "dependencies": { 29 | "angular": "latest" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/example/app/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Pusher Collaboration 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pusher-angular", 3 | "version": "0.1.0", 4 | "description": "An example Angular app using pusher-angular library", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/pusher/pusher-angular.git" 8 | }, 9 | "author": "Hamilton Chapman", 10 | "license": "MIT", 11 | "bugs": { 12 | "url": "https://github.com/pusher/pusher-angular/issues" 13 | }, 14 | "homepage": "http://github.com/pusher/pusher-angular", 15 | "engines": { 16 | "node": "0.10.26" 17 | }, 18 | "dependencies": { 19 | "body-parser": "^1.9.0", 20 | "ejs": "^1.0.0", 21 | "express": "^4.9.5", 22 | "pusher": "^1.0.1" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/example/public/components/Countable/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (C) 2014 Sacha Schmid 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /example/public/components/pusher-angular/example/public/components/Countable/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Countable", 3 | "description": "Countable is a JavaScript function to add live paragraph-, word- and character-counting to an HTML element.", 4 | "version": "2.0.2", 5 | "main": "Countable.js", 6 | "ignore": [ 7 | "test", 8 | "CONTRIBUTING.md", 9 | "CHANGELOG.md", 10 | "karma.conf.js", 11 | "package.json", 12 | ".travis.yml" 13 | ], 14 | "devDependencies": { 15 | "chai": "~1.7.2" 16 | } 17 | } -------------------------------------------------------------------------------- /example/public/components/pusher-angular/example/public/components/angular/angular-csp.css: -------------------------------------------------------------------------------- 1 | /* Include this file in your html if you are using the CSP mode. */ 2 | 3 | @charset "UTF-8"; 4 | 5 | [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], 6 | .ng-cloak, .x-ng-cloak, 7 | .ng-hide { 8 | display: none !important; 9 | } 10 | 11 | ng\:form { 12 | display: block; 13 | } 14 | 15 | .ng-animate-block-transitions { 16 | transition:0s all!important; 17 | -webkit-transition:0s all!important; 18 | } 19 | 20 | /* show the element during a show/hide animation when the 21 | * animation is ongoing, but the .ng-hide class is active */ 22 | .ng-hide-add-active, .ng-hide-remove { 23 | display: block!important; 24 | } 25 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/example/public/components/angular/angular.min.js.gzip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pusher/pusher-angular/26f48e0ed23e40f63cb8faa8b7a8bc70a716d95a/example/public/components/pusher-angular/example/public/components/angular/angular.min.js.gzip -------------------------------------------------------------------------------- /example/public/components/pusher-angular/example/public/components/angular/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular", 3 | "version": "1.2.26", 4 | "main": "./angular.js", 5 | "dependencies": { 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/example/public/components/angular/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular", 3 | "version": "", 4 | "description": "HTML enhanced for web apps", 5 | "main": "angular.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/angular/angular.js.git" 12 | }, 13 | "keywords": [ 14 | "angular", 15 | "framework", 16 | "browser", 17 | "client-side" 18 | ], 19 | "author": "Angular Core Team ", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/angular/angular.js/issues" 23 | }, 24 | "homepage": "http://angularjs.org" 25 | } 26 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/example/public/components/pusher-angular/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pusher-angular", 3 | "main": "lib/pusher-angular.js", 4 | "version": "0.1.1", 5 | "homepage": "https://github.com/pusher/pusher-angular", 6 | "authors": [ 7 | "Hamilton Chapman " 8 | ], 9 | "description": "The official Pusher Angular library", 10 | "keywords": [ 11 | "angular", 12 | "angularjs", 13 | "pusher", 14 | "realtime", 15 | "websocket" 16 | ], 17 | "license": "MIT", 18 | "ignore": [ 19 | "**/.*", 20 | "tests", 21 | "karma.conf.js", 22 | "README.md", 23 | "LICENSE" 24 | ], 25 | "devDependencies": { 26 | "angular": "latest", 27 | "angular-mocks": "latest" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/example/public/components/ui-router/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | ### 0.2.11 (2014-08-26) 3 | 4 | 5 | #### Bug Fixes 6 | 7 | * **$resolve:** Resolves only inherit from immediate parent fixes #702 ([df34e20c](https://github.com/angular-ui/ui-router/commit/df34e20c576299e7a3c8bd4ebc68d42341c0ace9)) 8 | * **$state:** 9 | * change $state.href default options.inherit to true ([deea695f](https://github.com/angular-ui/ui-router/commit/deea695f5cacc55de351ab985144fd233c02a769)) 10 | * sanity-check state lookups ([456fd5ae](https://github.com/angular-ui/ui-router/commit/456fd5aec9ea507518927bfabd62b4afad4cf714), closes [#980](https://github.com/angular-ui/ui-router/issues/980)) 11 | * didn't comply to inherit parameter ([09836781](https://github.com/angular-ui/ui-router/commit/09836781f126c1c485b06551eb9cfd4fa0f45c35)) 12 | * allow view content loading broadcast ([7b78edee](https://github.com/angular-ui/ui-router/commit/7b78edeeb52a74abf4d3f00f79534033d5a08d1a)) 13 | * **$urlMatcherFactory:** 14 | * detect injected functions ([91f75ae6](https://github.com/angular-ui/ui-router/commit/91f75ae66c4d129f6f69e53bd547594e9661f5d5)) 15 | * syntax ([1ebed370](https://github.com/angular-ui/ui-router/commit/1ebed37069bae8614d41541d56521f5c45f703f3)) 16 | * **UrlMatcher:** 17 | * query param function defaults ([f9c20530](https://github.com/angular-ui/ui-router/commit/f9c205304f10d8a4ebe7efe9025e642016479a51)) 18 | * don't decode default values ([63607bdb](https://github.com/angular-ui/ui-router/commit/63607bdbbcb432d3fb37856a1cb3da0cd496804e)) 19 | * **travis:** update Node version to fix build ([d6b95ef2](https://github.com/angular-ui/ui-router/commit/d6b95ef23d9dacb4eba08897f5190a0bcddb3a48)) 20 | * **uiSref:** 21 | * Generate an href for states with a blank url. closes #1293 ([691745b1](https://github.com/angular-ui/ui-router/commit/691745b12fa05d3700dd28f0c8d25f8a105074ad)) 22 | * should inherit params by default ([b973dad1](https://github.com/angular-ui/ui-router/commit/b973dad155ad09a7975e1476bd096f7b2c758eeb)) 23 | * cancel transition if preventDefault() has been called ([2e6d9167](https://github.com/angular-ui/ui-router/commit/2e6d9167d3afbfbca6427e53e012f94fb5fb8022)) 24 | * **uiView:** Fixed infinite loop when is called .go() from a controller. ([e13988b8](https://github.com/angular-ui/ui-router/commit/e13988b8cd6231d75c78876ee9d012cc87f4a8d9), closes [#1194](https://github.com/angular-ui/ui-router/issues/1194)) 25 | * **docs:** 26 | * Fixed link to milestones ([6c0ae500](https://github.com/angular-ui/ui-router/commit/6c0ae500cc238ea9fc95adcc15415c55fc9e1f33)) 27 | * fix bug in decorator example ([4bd00af5](https://github.com/angular-ui/ui-router/commit/4bd00af50b8b88a49d1545a76290731cb8e0feb1)) 28 | * Removed an incorrect semi-colon ([af97cef8](https://github.com/angular-ui/ui-router/commit/af97cef8b967f2e32177e539ef41450dca131a7d)) 29 | * Explain return value of rule as function ([5e887890](https://github.com/angular-ui/ui-router/commit/5e8878900a6ffe59a81aed531a3925e34a297377)) 30 | 31 | 32 | #### Features 33 | 34 | * **$state:** 35 | * allow parameters to pass unharmed ([8939d057](https://github.com/angular-ui/ui-router/commit/8939d0572ab1316e458ef016317ecff53131a822)) 36 | * allow prevent syncUrl on failure ([753060b9](https://github.com/angular-ui/ui-router/commit/753060b910d5d2da600a6fa0757976e401c33172)) 37 | * **typescript:** Add typescript definitions for component builds ([521ceb3f](https://github.com/angular-ui/ui-router/commit/521ceb3fd7850646422f411921e21ce5e7d82e0f)) 38 | * **uiSref:** extend syntax for ui-sref ([71cad3d6](https://github.com/angular-ui/ui-router/commit/71cad3d636508b5a9fe004775ad1f1adc0c80c3e)) 39 | * **uiSrefActive:** 40 | * **BREAKING CHANGE** Also activate for child states. ([bf163ad6](https://github.com/angular-ui/ui-router/commit/bf163ad6ce176ce28792696c8302d7cdf5c05a01), closes [#818](https://github.com/angular-ui/ui-router/issues/818)) 41 | * uiSrefActiveEq: new directive with old ui-sref-active behavior 42 | * **$urlRouter:** 43 | * defer URL change interception ([c72d8ce1](https://github.com/angular-ui/ui-router/commit/c72d8ce11916d0ac22c81b409c9e61d7048554d7)) 44 | * force URLs to have valid params ([d48505cd](https://github.com/angular-ui/ui-router/commit/d48505cd328d83e39d5706e085ba319715f999a6)) 45 | * abstract $location handling ([08b4636b](https://github.com/angular-ui/ui-router/commit/08b4636b294611f08db35f00641eb5211686fb50)) 46 | * **$urlMatcherFactory:** 47 | * fail on bad parameters ([d8f124c1](https://github.com/angular-ui/ui-router/commit/d8f124c10d00c7e5dde88c602d966db261aea221)) 48 | * date type support ([b7f074ff](https://github.com/angular-ui/ui-router/commit/b7f074ff65ca150a3cdbda4d5ad6cb17107300eb)) 49 | * implement type support ([450b1f0e](https://github.com/angular-ui/ui-router/commit/450b1f0e8e03c738174ff967f688b9a6373290f4)) 50 | * **UrlMatcher:** 51 | * handle query string arrays ([9cf764ef](https://github.com/angular-ui/ui-router/commit/9cf764efab45fa9309368688d535ddf6e96d6449), closes [#373](https://github.com/angular-ui/ui-router/issues/373)) 52 | * injectable functions as defaults ([00966ecd](https://github.com/angular-ui/ui-router/commit/00966ecd91fb745846039160cab707bfca8b3bec)) 53 | * default values & type decoding for query params ([a472b301](https://github.com/angular-ui/ui-router/commit/a472b301389fbe84d1c1fa9f24852b492a569d11)) 54 | * allow shorthand definitions ([5b724304](https://github.com/angular-ui/ui-router/commit/5b7243049793505e44b6608ea09878c37c95b1f5)) 55 | * validates whole interface ([32b27db1](https://github.com/angular-ui/ui-router/commit/32b27db173722e9194ef1d5c0ea7d93f25a98d11)) 56 | * implement non-strict matching ([a3e21366](https://github.com/angular-ui/ui-router/commit/a3e21366bee0475c9795a1ec76f70eec41c5b4e3)) 57 | * add per-param config support ([07b3029f](https://github.com/angular-ui/ui-router/commit/07b3029f4d409cf955780113df92e36401b47580)) 58 | 59 | 60 | ### 0.2.10 (2014-03-12) 61 | 62 | 63 | #### Bug Fixes 64 | 65 | * **$state:** use $browser.baseHref() when generating urls with .href() ([cbcc8488](https://github.com/angular-ui/ui-router/commit/cbcc84887d6b6d35258adabb97c714cd9c1e272d)) 66 | * **bower.json:** JS files should not be ignored ([ccdab193](https://github.com/angular-ui/ui-router/commit/ccdab193315f304eb3be5f5b97c47a926c79263e)) 67 | * **dev:** karma:background task is missing, can't run grunt:dev. ([d9f7b898](https://github.com/angular-ui/ui-router/commit/d9f7b898e8e3abb8c846b0faa16a382913d7b22b)) 68 | * **sample:** Contacts menu button not staying active when navigating to detail states. Need t ([2fcb8443](https://github.com/angular-ui/ui-router/commit/2fcb84437cb43ade12682a92b764f13cac77dfe7)) 69 | * **uiSref:** support mock-clicks/events with no data ([717d3ff7](https://github.com/angular-ui/ui-router/commit/717d3ff7d0ba72d239892dee562b401cdf90e418)) 70 | * **uiView:** 71 | * Do NOT autoscroll when autoscroll attr is missing ([affe5bd7](https://github.com/angular-ui/ui-router/commit/affe5bd785cdc3f02b7a9f64a52e3900386ec3a0), closes [#807](https://github.com/angular-ui/ui-router/issues/807)) 72 | * Refactoring uiView directive to copy ngView logic ([548fab6a](https://github.com/angular-ui/ui-router/commit/548fab6ab9debc9904c5865c8bc68b4fc3271dd0), closes [#857](https://github.com/angular-ui/ui-router/issues/857), [#552](https://github.com/angular-ui/ui-router/issues/552)) 73 | 74 | 75 | #### Features 76 | 77 | * **$state:** includes() allows glob patterns for state matching. ([2d5f6b37](https://github.com/angular-ui/ui-router/commit/2d5f6b37191a3135f4a6d9e8f344c54edcdc065b)) 78 | * **UrlMatcher:** Add support for case insensitive url matching ([642d5247](https://github.com/angular-ui/ui-router/commit/642d524799f604811e680331002feec7199a1fb5)) 79 | * **uiSref:** add support for transition options ([2ed7a728](https://github.com/angular-ui/ui-router/commit/2ed7a728cee6854b38501fbc1df6139d3de5b28a)) 80 | * **uiView:** add controllerAs config with function ([1ee7334a](https://github.com/angular-ui/ui-router/commit/1ee7334a73efeccc9b95340e315cdfd59944762d)) 81 | 82 | 83 | ### 0.2.9 (2014-01-17) 84 | 85 | 86 | This release is identical to 0.2.8. 0.2.8 was re-tagged in git to fix a problem with bower. 87 | 88 | 89 | ### 0.2.8 (2014-01-16) 90 | 91 | 92 | #### Bug Fixes 93 | 94 | * **$state:** allow null to be passed as 'params' param ([094dc30e](https://github.com/angular-ui/ui-router/commit/094dc30e883e1bd14e50a475553bafeaade3b178)) 95 | * **$state.go:** param inheritance shouldn't inherit from siblings ([aea872e0](https://github.com/angular-ui/ui-router/commit/aea872e0b983cb433436ce5875df10c838fccedb)) 96 | * **bower.json:** fixes bower.json ([eed3cc4d](https://github.com/angular-ui/ui-router/commit/eed3cc4d4dfef1d3ef84b9fd063127538ebf59d3)) 97 | * **uiSrefActive:** annotate controller injection ([85921422](https://github.com/angular-ui/ui-router/commit/85921422ff7fb0effed358136426d616cce3d583), closes [#671](https://github.com/angular-ui/ui-router/issues/671)) 98 | * **uiView:** 99 | * autoscroll tests pass on 1.2.4 & 1.1.5 ([86eacac0](https://github.com/angular-ui/ui-router/commit/86eacac09ca5e9000bd3b9c7ba6e2cc95d883a3a)) 100 | * don't animate initial load ([83b6634d](https://github.com/angular-ui/ui-router/commit/83b6634d27942ca74766b2b1244a7fc52c5643d9)) 101 | * test pass against 1.0.8 and 1.2.4 ([a402415a](https://github.com/angular-ui/ui-router/commit/a402415a2a28b360c43b9fe8f4f54c540f6c33de)) 102 | * it should autoscroll when expr is missing. ([8bb9e27a](https://github.com/angular-ui/ui-router/commit/8bb9e27a2986725f45daf44c4c9f846385095aff)) 103 | 104 | 105 | #### Features 106 | 107 | * **uiSref:** add target attribute behaviour ([c12bf9a5](https://github.com/angular-ui/ui-router/commit/c12bf9a520d30d70294e3d82de7661900f8e394e)) 108 | * **uiView:** 109 | * merge autoscroll expression test. ([b89e0f87](https://github.com/angular-ui/ui-router/commit/b89e0f871d5cc35c10925ede986c10684d5c9252)) 110 | * cache and test autoscroll expression ([ee262282](https://github.com/angular-ui/ui-router/commit/ee2622828c2ce83807f006a459ac4e11406d9258)) 111 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/example/public/components/ui-router/api/angular-ui-router.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for Angular JS 1.1.5+ (ui.router module) 2 | // Project: https://github.com/angular-ui/ui-router 3 | // Definitions by: Michel Salib 4 | // Definitions: https://github.com/borisyankov/DefinitelyTyped 5 | 6 | declare module ng.ui { 7 | 8 | interface IState { 9 | name?: string; 10 | template?: string; 11 | templateUrl?: any; // string || () => string 12 | templateProvider?: any; // () => string || IPromise 13 | controller?: any; 14 | controllerAs?: string; 15 | controllerProvider?: any; 16 | resolve?: {}; 17 | url?: string; 18 | params?: any[]; 19 | views?: {}; 20 | abstract?: boolean; 21 | onEnter?: (...args: any[]) => void; 22 | onExit?: (...args: any[]) => void; 23 | data?: any; 24 | } 25 | 26 | interface ITypedState { 27 | name?: string; 28 | template?: string; 29 | templateUrl?: string; 30 | templateProvider?: () => string; 31 | controller?: any; 32 | controllerAs?: string; 33 | controllerProvider?: any; 34 | resolve?: {}; 35 | url?: string; 36 | params?: any[]; 37 | views?: {}; 38 | abstract?: boolean; 39 | onEnter?: (...args: any[]) => void; 40 | onExit?: (...args: any[]) => void; 41 | data?: T; 42 | } 43 | 44 | interface IStateProvider extends IServiceProvider { 45 | state(name: string, config: IState): IStateProvider; 46 | state(config: IState): IStateProvider; 47 | decorator(name?: string, decorator?: (state: IState, parent: Function) => any): any; 48 | } 49 | 50 | interface IUrlMatcher { 51 | concat(pattern: string): IUrlMatcher; 52 | exec(path: string, searchParams: {}): {}; 53 | parameters(): string[]; 54 | format(values: {}): string; 55 | } 56 | 57 | interface IUrlMatcherFactory { 58 | compile(pattern: string): IUrlMatcher; 59 | isMatcher(o: any): boolean; 60 | } 61 | 62 | interface IUrlRouterProvider extends IServiceProvider { 63 | when(whenPath: RegExp, handler: Function): IUrlRouterProvider; 64 | when(whenPath: RegExp, handler: any[]): IUrlRouterProvider; 65 | when(whenPath: RegExp, toPath: string): IUrlRouterProvider; 66 | when(whenPath: IUrlMatcher, hanlder: Function): IUrlRouterProvider; 67 | when(whenPath: IUrlMatcher, handler: any[]): IUrlRouterProvider; 68 | when(whenPath: IUrlMatcher, toPath: string): IUrlRouterProvider; 69 | when(whenPath: string, handler: Function): IUrlRouterProvider; 70 | when(whenPath: string, handler: any[]): IUrlRouterProvider; 71 | when(whenPath: string, toPath: string): IUrlRouterProvider; 72 | otherwise(handler: Function): IUrlRouterProvider; 73 | otherwise(handler: any[]): IUrlRouterProvider; 74 | otherwise(path: string): IUrlRouterProvider; 75 | rule(handler: Function): IUrlRouterProvider; 76 | rule(handler: any[]): IUrlRouterProvider; 77 | } 78 | 79 | interface IStateOptions { 80 | location?: any; 81 | inherit?: boolean; 82 | relative?: IState; 83 | notify?: boolean; 84 | } 85 | 86 | interface IHrefOptions { 87 | lossy?: boolean; 88 | inherit?: boolean; 89 | relative?: IState; 90 | absolute?: boolean; 91 | } 92 | 93 | interface IStateService { 94 | go(to: string, params?: {}, options?: IStateOptions): IPromise; 95 | transitionTo(state: string, params?: {}, updateLocation?: boolean): void; 96 | transitionTo(state: string, params?: {}, options?: IStateOptions): void; 97 | includes(state: string, params?: {}): boolean; 98 | is(state:string, params?: {}): boolean; 99 | is(state: IState, params?: {}): boolean; 100 | href(state: IState, params?: {}, options?: IHrefOptions): string; 101 | href(state: string, params?: {}, options?: IHrefOptions): string; 102 | get(state: string): IState; 103 | get(): IState[]; 104 | current: IState; 105 | params: any; 106 | reload(): void; 107 | } 108 | 109 | interface IStateParamsService { 110 | [key: string]: any; 111 | } 112 | 113 | interface IStateParams { 114 | [key: string]: any; 115 | } 116 | 117 | interface IUrlRouterService { 118 | /* 119 | * Triggers an update; the same update that happens when the address bar 120 | * url changes, aka $locationChangeSuccess. 121 | * 122 | * This method is useful when you need to use preventDefault() on the 123 | * $locationChangeSuccess event, perform some custom logic (route protection, 124 | * auth, config, redirection, etc) and then finally proceed with the transition 125 | * by calling $urlRouter.sync(). 126 | * 127 | */ 128 | sync(): void; 129 | } 130 | 131 | interface IUiViewScrollProvider { 132 | /* 133 | * Reverts back to using the core $anchorScroll service for scrolling 134 | * based on the url anchor. 135 | */ 136 | useAnchorScroll(): void; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/example/public/components/ui-router/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-ui-router", 3 | "version": "0.2.11", 4 | "main": "./release/angular-ui-router.js", 5 | "dependencies": { 6 | "angular": ">= 1.0.8" 7 | }, 8 | "ignore": [ 9 | "**/.*", 10 | "node_modules", 11 | "bower_components", 12 | "component.json", 13 | "package.json", 14 | "lib", 15 | "config", 16 | "sample", 17 | "test", 18 | "tests", 19 | "ngdoc_assets", 20 | "Gruntfile.js", 21 | "files.js" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/example/public/components/ui-router/src/common.js: -------------------------------------------------------------------------------- 1 | /*jshint globalstrict:true*/ 2 | /*global angular:false*/ 3 | 'use strict'; 4 | 5 | var isDefined = angular.isDefined, 6 | isFunction = angular.isFunction, 7 | isString = angular.isString, 8 | isObject = angular.isObject, 9 | isArray = angular.isArray, 10 | forEach = angular.forEach, 11 | extend = angular.extend, 12 | copy = angular.copy; 13 | 14 | function inherit(parent, extra) { 15 | return extend(new (extend(function() {}, { prototype: parent }))(), extra); 16 | } 17 | 18 | function merge(dst) { 19 | forEach(arguments, function(obj) { 20 | if (obj !== dst) { 21 | forEach(obj, function(value, key) { 22 | if (!dst.hasOwnProperty(key)) dst[key] = value; 23 | }); 24 | } 25 | }); 26 | return dst; 27 | } 28 | 29 | /** 30 | * Finds the common ancestor path between two states. 31 | * 32 | * @param {Object} first The first state. 33 | * @param {Object} second The second state. 34 | * @return {Array} Returns an array of state names in descending order, not including the root. 35 | */ 36 | function ancestors(first, second) { 37 | var path = []; 38 | 39 | for (var n in first.path) { 40 | if (first.path[n] !== second.path[n]) break; 41 | path.push(first.path[n]); 42 | } 43 | return path; 44 | } 45 | 46 | /** 47 | * IE8-safe wrapper for `Object.keys()`. 48 | * 49 | * @param {Object} object A JavaScript object. 50 | * @return {Array} Returns the keys of the object as an array. 51 | */ 52 | function objectKeys(object) { 53 | if (Object.keys) { 54 | return Object.keys(object); 55 | } 56 | var result = []; 57 | 58 | angular.forEach(object, function(val, key) { 59 | result.push(key); 60 | }); 61 | return result; 62 | } 63 | 64 | /** 65 | * IE8-safe wrapper for `Array.prototype.indexOf()`. 66 | * 67 | * @param {Array} array A JavaScript array. 68 | * @param {*} value A value to search the array for. 69 | * @return {Number} Returns the array index value of `value`, or `-1` if not present. 70 | */ 71 | function arraySearch(array, value) { 72 | if (Array.prototype.indexOf) { 73 | return array.indexOf(value, Number(arguments[2]) || 0); 74 | } 75 | var len = array.length >>> 0, from = Number(arguments[2]) || 0; 76 | from = (from < 0) ? Math.ceil(from) : Math.floor(from); 77 | 78 | if (from < 0) from += len; 79 | 80 | for (; from < len; from++) { 81 | if (from in array && array[from] === value) return from; 82 | } 83 | return -1; 84 | } 85 | 86 | /** 87 | * Merges a set of parameters with all parameters inherited between the common parents of the 88 | * current state and a given destination state. 89 | * 90 | * @param {Object} currentParams The value of the current state parameters ($stateParams). 91 | * @param {Object} newParams The set of parameters which will be composited with inherited params. 92 | * @param {Object} $current Internal definition of object representing the current state. 93 | * @param {Object} $to Internal definition of object representing state to transition to. 94 | */ 95 | function inheritParams(currentParams, newParams, $current, $to) { 96 | var parents = ancestors($current, $to), parentParams, inherited = {}, inheritList = []; 97 | 98 | for (var i in parents) { 99 | if (!parents[i].params) continue; 100 | parentParams = objectKeys(parents[i].params); 101 | if (!parentParams.length) continue; 102 | 103 | for (var j in parentParams) { 104 | if (arraySearch(inheritList, parentParams[j]) >= 0) continue; 105 | inheritList.push(parentParams[j]); 106 | inherited[parentParams[j]] = currentParams[parentParams[j]]; 107 | } 108 | } 109 | return extend({}, inherited, newParams); 110 | } 111 | 112 | /** 113 | * Performs a non-strict comparison of the subset of two objects, defined by a list of keys. 114 | * 115 | * @param {Object} a The first object. 116 | * @param {Object} b The second object. 117 | * @param {Array} keys The list of keys within each object to compare. If the list is empty or not specified, 118 | * it defaults to the list of keys in `a`. 119 | * @return {Boolean} Returns `true` if the keys match, otherwise `false`. 120 | */ 121 | function equalForKeys(a, b, keys) { 122 | if (!keys) { 123 | keys = []; 124 | for (var n in a) keys.push(n); // Used instead of Object.keys() for IE8 compatibility 125 | } 126 | 127 | for (var i=0; i 212 | * 213 | * 214 | * 215 | * 216 | * 217 | * 218 | * 222 | * 223 | * 224 | * 225 | * 226 | * 227 | */ 228 | angular.module('ui.router', ['ui.router.state']); 229 | 230 | angular.module('ui.router.compat', ['ui.router']); 231 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/example/public/components/ui-router/src/resolve.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @ngdoc object 3 | * @name ui.router.util.$resolve 4 | * 5 | * @requires $q 6 | * @requires $injector 7 | * 8 | * @description 9 | * Manages resolution of (acyclic) graphs of promises. 10 | */ 11 | $Resolve.$inject = ['$q', '$injector']; 12 | function $Resolve( $q, $injector) { 13 | 14 | var VISIT_IN_PROGRESS = 1, 15 | VISIT_DONE = 2, 16 | NOTHING = {}, 17 | NO_DEPENDENCIES = [], 18 | NO_LOCALS = NOTHING, 19 | NO_PARENT = extend($q.when(NOTHING), { $$promises: NOTHING, $$values: NOTHING }); 20 | 21 | 22 | /** 23 | * @ngdoc function 24 | * @name ui.router.util.$resolve#study 25 | * @methodOf ui.router.util.$resolve 26 | * 27 | * @description 28 | * Studies a set of invocables that are likely to be used multiple times. 29 | *
 30 |    * $resolve.study(invocables)(locals, parent, self)
 31 |    * 
32 | * is equivalent to 33 | *
 34 |    * $resolve.resolve(invocables, locals, parent, self)
 35 |    * 
36 | * but the former is more efficient (in fact `resolve` just calls `study` 37 | * internally). 38 | * 39 | * @param {object} invocables Invocable objects 40 | * @return {function} a function to pass in locals, parent and self 41 | */ 42 | this.study = function (invocables) { 43 | if (!isObject(invocables)) throw new Error("'invocables' must be an object"); 44 | 45 | // Perform a topological sort of invocables to build an ordered plan 46 | var plan = [], cycle = [], visited = {}; 47 | function visit(value, key) { 48 | if (visited[key] === VISIT_DONE) return; 49 | 50 | cycle.push(key); 51 | if (visited[key] === VISIT_IN_PROGRESS) { 52 | cycle.splice(0, cycle.indexOf(key)); 53 | throw new Error("Cyclic dependency: " + cycle.join(" -> ")); 54 | } 55 | visited[key] = VISIT_IN_PROGRESS; 56 | 57 | if (isString(value)) { 58 | plan.push(key, [ function() { return $injector.get(value); }], NO_DEPENDENCIES); 59 | } else { 60 | var params = $injector.annotate(value); 61 | forEach(params, function (param) { 62 | if (param !== key && invocables.hasOwnProperty(param)) visit(invocables[param], param); 63 | }); 64 | plan.push(key, value, params); 65 | } 66 | 67 | cycle.pop(); 68 | visited[key] = VISIT_DONE; 69 | } 70 | forEach(invocables, visit); 71 | invocables = cycle = visited = null; // plan is all that's required 72 | 73 | function isResolve(value) { 74 | return isObject(value) && value.then && value.$$promises; 75 | } 76 | 77 | return function (locals, parent, self) { 78 | if (isResolve(locals) && self === undefined) { 79 | self = parent; parent = locals; locals = null; 80 | } 81 | if (!locals) locals = NO_LOCALS; 82 | else if (!isObject(locals)) { 83 | throw new Error("'locals' must be an object"); 84 | } 85 | if (!parent) parent = NO_PARENT; 86 | else if (!isResolve(parent)) { 87 | throw new Error("'parent' must be a promise returned by $resolve.resolve()"); 88 | } 89 | 90 | // To complete the overall resolution, we have to wait for the parent 91 | // promise and for the promise for each invokable in our plan. 92 | var resolution = $q.defer(), 93 | result = resolution.promise, 94 | promises = result.$$promises = {}, 95 | values = extend({}, locals), 96 | wait = 1 + plan.length/3, 97 | merged = false; 98 | 99 | function done() { 100 | // Merge parent values we haven't got yet and publish our own $$values 101 | if (!--wait) { 102 | if (!merged) merge(values, parent.$$values); 103 | result.$$values = values; 104 | result.$$promises = true; // keep for isResolve() 105 | delete result.$$inheritedValues; 106 | resolution.resolve(values); 107 | } 108 | } 109 | 110 | function fail(reason) { 111 | result.$$failure = reason; 112 | resolution.reject(reason); 113 | } 114 | 115 | // Short-circuit if parent has already failed 116 | if (isDefined(parent.$$failure)) { 117 | fail(parent.$$failure); 118 | return result; 119 | } 120 | 121 | if (parent.$$inheritedValues) { 122 | merge(values, parent.$$inheritedValues); 123 | } 124 | 125 | // Merge parent values if the parent has already resolved, or merge 126 | // parent promises and wait if the parent resolve is still in progress. 127 | if (parent.$$values) { 128 | merged = merge(values, parent.$$values); 129 | result.$$inheritedValues = parent.$$values; 130 | done(); 131 | } else { 132 | if (parent.$$inheritedValues) { 133 | result.$$inheritedValues = parent.$$inheritedValues; 134 | } 135 | extend(promises, parent.$$promises); 136 | parent.then(done, fail); 137 | } 138 | 139 | // Process each invocable in the plan, but ignore any where a local of the same name exists. 140 | for (var i=0, ii=plan.length; i` tag) to a state. If the state has an associated 28 | * URL, the directive will automatically generate & update the `href` attribute via 29 | * the {@link ui.router.state.$state#methods_href $state.href()} method. Clicking 30 | * the link will trigger a state transition with optional parameters. 31 | * 32 | * Also middle-clicking, right-clicking, and ctrl-clicking on the link will be 33 | * handled natively by the browser. 34 | * 35 | * You can also use relative state paths within ui-sref, just like the relative 36 | * paths passed to `$state.go()`. You just need to be aware that the path is relative 37 | * to the state that the link lives in, in other words the state that loaded the 38 | * template containing the link. 39 | * 40 | * You can specify options to pass to {@link ui.router.state.$state#go $state.go()} 41 | * using the `ui-sref-opts` attribute. Options are restricted to `location`, `inherit`, 42 | * and `reload`. 43 | * 44 | * @example 45 | * Here's an example of how you'd use ui-sref and how it would compile. If you have the 46 | * following template: 47 | *
 48 |  * Home | About | Next page
 49 |  * 
 50 |  * 
 55 |  * 
56 | * 57 | * Then the compiled html would be (assuming Html5Mode is off and current state is contacts): 58 | *
 59 |  * Home | About | Next page
 60 |  * 
 61 |  * 
    62 | *
  • 63 | * Joe 64 | *
  • 65 | *
  • 66 | * Alice 67 | *
  • 68 | *
  • 69 | * Bob 70 | *
  • 71 | *
72 | * 73 | * Home 74 | *
75 | * 76 | * @param {string} ui-sref 'stateName' can be any valid absolute or relative state 77 | * @param {Object} ui-sref-opts options to pass to {@link ui.router.state.$state#go $state.go()} 78 | */ 79 | $StateRefDirective.$inject = ['$state', '$timeout']; 80 | function $StateRefDirective($state, $timeout) { 81 | var allowedOptions = ['location', 'inherit', 'reload']; 82 | 83 | return { 84 | restrict: 'A', 85 | require: ['?^uiSrefActive', '?^uiSrefActiveEq'], 86 | link: function(scope, element, attrs, uiSrefActive) { 87 | var ref = parseStateRef(attrs.uiSref, $state.current.name); 88 | var params = null, url = null, base = stateContext(element) || $state.$current; 89 | var isForm = element[0].nodeName === "FORM"; 90 | var attr = isForm ? "action" : "href", nav = true; 91 | 92 | var options = { relative: base, inherit: true }; 93 | var optionsOverride = scope.$eval(attrs.uiSrefOpts) || {}; 94 | 95 | angular.forEach(allowedOptions, function(option) { 96 | if (option in optionsOverride) { 97 | options[option] = optionsOverride[option]; 98 | } 99 | }); 100 | 101 | var update = function(newVal) { 102 | if (newVal) params = newVal; 103 | if (!nav) return; 104 | 105 | var newHref = $state.href(ref.state, params, options); 106 | 107 | var activeDirective = uiSrefActive[1] || uiSrefActive[0]; 108 | if (activeDirective) { 109 | activeDirective.$$setStateInfo(ref.state, params); 110 | } 111 | if (newHref === null) { 112 | nav = false; 113 | return false; 114 | } 115 | element[0][attr] = newHref; 116 | }; 117 | 118 | if (ref.paramExpr) { 119 | scope.$watch(ref.paramExpr, function(newVal, oldVal) { 120 | if (newVal !== params) update(newVal); 121 | }, true); 122 | params = scope.$eval(ref.paramExpr); 123 | } 124 | update(); 125 | 126 | if (isForm) return; 127 | 128 | element.bind("click", function(e) { 129 | var button = e.which || e.button; 130 | if ( !(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || element.attr('target')) ) { 131 | // HACK: This is to allow ng-clicks to be processed before the transition is initiated: 132 | var transition = $timeout(function() { 133 | $state.go(ref.state, params, options); 134 | }); 135 | e.preventDefault(); 136 | 137 | e.preventDefault = function() { 138 | $timeout.cancel(transition); 139 | }; 140 | } 141 | }); 142 | } 143 | }; 144 | } 145 | 146 | /** 147 | * @ngdoc directive 148 | * @name ui.router.state.directive:ui-sref-active 149 | * 150 | * @requires ui.router.state.$state 151 | * @requires ui.router.state.$stateParams 152 | * @requires $interpolate 153 | * 154 | * @restrict A 155 | * 156 | * @description 157 | * A directive working alongside ui-sref to add classes to an element when the 158 | * related ui-sref directive's state is active, and removing them when it is inactive. 159 | * The primary use-case is to simplify the special appearance of navigation menus 160 | * relying on `ui-sref`, by having the "active" state's menu button appear different, 161 | * distinguishing it from the inactive menu items. 162 | * 163 | * ui-sref-active can live on the same element as ui-sref or on a parent element. The first 164 | * ui-sref-active found at the same level or above the ui-sref will be used. 165 | * 166 | * Will activate when the ui-sref's target state or any child state is active. If you 167 | * need to activate only when the ui-sref target state is active and *not* any of 168 | * it's children, then you will use 169 | * {@link ui.router.state.directive:ui-sref-active-eq ui-sref-active-eq} 170 | * 171 | * @example 172 | * Given the following template: 173 | *
174 |  * 
179 |  * 
180 | * 181 | * 182 | * When the app state is "app.user" (or any children states), and contains the state parameter "user" with value "bilbobaggins", 183 | * the resulting HTML will appear as (note the 'active' class): 184 | *
185 |  * 
190 |  * 
191 | * 192 | * The class name is interpolated **once** during the directives link time (any further changes to the 193 | * interpolated value are ignored). 194 | * 195 | * Multiple classes may be specified in a space-separated format: 196 | *
197 |  * 
    198 | *
  • 199 | * link 200 | *
  • 201 | *
202 | *
203 | */ 204 | 205 | /** 206 | * @ngdoc directive 207 | * @name ui.router.state.directive:ui-sref-active-eq 208 | * 209 | * @requires ui.router.state.$state 210 | * @requires ui.router.state.$stateParams 211 | * @requires $interpolate 212 | * 213 | * @restrict A 214 | * 215 | * @description 216 | * The same as {@link ui.router.state.directive:ui-sref-active ui-sref-active} but will will only activate 217 | * when the exact target state used in the `ui-sref` is active; no child states. 218 | * 219 | */ 220 | $StateRefActiveDirective.$inject = ['$state', '$stateParams', '$interpolate']; 221 | function $StateRefActiveDirective($state, $stateParams, $interpolate) { 222 | return { 223 | restrict: "A", 224 | controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) { 225 | var state, params, activeClass; 226 | 227 | // There probably isn't much point in $observing this 228 | // uiSrefActive and uiSrefActiveEq share the same directive object with some 229 | // slight difference in logic routing 230 | activeClass = $interpolate($attrs.uiSrefActiveEq || $attrs.uiSrefActive || '', false)($scope); 231 | 232 | // Allow uiSref to communicate with uiSrefActive[Equals] 233 | this.$$setStateInfo = function (newState, newParams) { 234 | state = $state.get(newState, stateContext($element)); 235 | params = newParams; 236 | update(); 237 | }; 238 | 239 | $scope.$on('$stateChangeSuccess', update); 240 | 241 | // Update route state 242 | function update() { 243 | if (isMatch()) { 244 | $element.addClass(activeClass); 245 | } else { 246 | $element.removeClass(activeClass); 247 | } 248 | } 249 | 250 | function isMatch() { 251 | if (typeof $attrs.uiSrefActiveEq !== 'undefined') { 252 | return $state.$current.self === state && matchesParams(); 253 | } else { 254 | return $state.includes(state.name) && matchesParams(); 255 | } 256 | } 257 | 258 | function matchesParams() { 259 | return !params || equalForKeys(params, $stateParams); 260 | } 261 | }] 262 | }; 263 | } 264 | 265 | angular.module('ui.router.state') 266 | .directive('uiSref', $StateRefDirective) 267 | .directive('uiSrefActive', $StateRefActiveDirective) 268 | .directive('uiSrefActiveEq', $StateRefActiveDirective); 269 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/example/public/components/ui-router/src/stateFilters.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @ngdoc filter 3 | * @name ui.router.state.filter:isState 4 | * 5 | * @requires ui.router.state.$state 6 | * 7 | * @description 8 | * Translates to {@link ui.router.state.$state#methods_is $state.is("stateName")}. 9 | */ 10 | $IsStateFilter.$inject = ['$state']; 11 | function $IsStateFilter($state) { 12 | return function(state) { 13 | return $state.is(state); 14 | }; 15 | } 16 | 17 | /** 18 | * @ngdoc filter 19 | * @name ui.router.state.filter:includedByState 20 | * 21 | * @requires ui.router.state.$state 22 | * 23 | * @description 24 | * Translates to {@link ui.router.state.$state#methods_includes $state.includes('fullOrPartialStateName')}. 25 | */ 26 | $IncludedByStateFilter.$inject = ['$state']; 27 | function $IncludedByStateFilter($state) { 28 | return function(state) { 29 | return $state.includes(state); 30 | }; 31 | } 32 | 33 | angular.module('ui.router.state') 34 | .filter('isState', $IsStateFilter) 35 | .filter('includedByState', $IncludedByStateFilter); 36 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/example/public/components/ui-router/src/templateFactory.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @ngdoc object 3 | * @name ui.router.util.$templateFactory 4 | * 5 | * @requires $http 6 | * @requires $templateCache 7 | * @requires $injector 8 | * 9 | * @description 10 | * Service. Manages loading of templates. 11 | */ 12 | $TemplateFactory.$inject = ['$http', '$templateCache', '$injector']; 13 | function $TemplateFactory( $http, $templateCache, $injector) { 14 | 15 | /** 16 | * @ngdoc function 17 | * @name ui.router.util.$templateFactory#fromConfig 18 | * @methodOf ui.router.util.$templateFactory 19 | * 20 | * @description 21 | * Creates a template from a configuration object. 22 | * 23 | * @param {object} config Configuration object for which to load a template. 24 | * The following properties are search in the specified order, and the first one 25 | * that is defined is used to create the template: 26 | * 27 | * @param {string|object} config.template html string template or function to 28 | * load via {@link ui.router.util.$templateFactory#fromString fromString}. 29 | * @param {string|object} config.templateUrl url to load or a function returning 30 | * the url to load via {@link ui.router.util.$templateFactory#fromUrl fromUrl}. 31 | * @param {Function} config.templateProvider function to invoke via 32 | * {@link ui.router.util.$templateFactory#fromProvider fromProvider}. 33 | * @param {object} params Parameters to pass to the template function. 34 | * @param {object} locals Locals to pass to `invoke` if the template is loaded 35 | * via a `templateProvider`. Defaults to `{ params: params }`. 36 | * 37 | * @return {string|object} The template html as a string, or a promise for 38 | * that string,or `null` if no template is configured. 39 | */ 40 | this.fromConfig = function (config, params, locals) { 41 | return ( 42 | isDefined(config.template) ? this.fromString(config.template, params) : 43 | isDefined(config.templateUrl) ? this.fromUrl(config.templateUrl, params) : 44 | isDefined(config.templateProvider) ? this.fromProvider(config.templateProvider, params, locals) : 45 | null 46 | ); 47 | }; 48 | 49 | /** 50 | * @ngdoc function 51 | * @name ui.router.util.$templateFactory#fromString 52 | * @methodOf ui.router.util.$templateFactory 53 | * 54 | * @description 55 | * Creates a template from a string or a function returning a string. 56 | * 57 | * @param {string|object} template html template as a string or function that 58 | * returns an html template as a string. 59 | * @param {object} params Parameters to pass to the template function. 60 | * 61 | * @return {string|object} The template html as a string, or a promise for that 62 | * string. 63 | */ 64 | this.fromString = function (template, params) { 65 | return isFunction(template) ? template(params) : template; 66 | }; 67 | 68 | /** 69 | * @ngdoc function 70 | * @name ui.router.util.$templateFactory#fromUrl 71 | * @methodOf ui.router.util.$templateFactory 72 | * 73 | * @description 74 | * Loads a template from the a URL via `$http` and `$templateCache`. 75 | * 76 | * @param {string|Function} url url of the template to load, or a function 77 | * that returns a url. 78 | * @param {Object} params Parameters to pass to the url function. 79 | * @return {string|Promise.} The template html as a string, or a promise 80 | * for that string. 81 | */ 82 | this.fromUrl = function (url, params) { 83 | if (isFunction(url)) url = url(params); 84 | if (url == null) return null; 85 | else return $http 86 | .get(url, { cache: $templateCache }) 87 | .then(function(response) { return response.data; }); 88 | }; 89 | 90 | /** 91 | * @ngdoc function 92 | * @name ui.router.util.$templateFactory#fromProvider 93 | * @methodOf ui.router.util.$templateFactory 94 | * 95 | * @description 96 | * Creates a template by invoking an injectable provider function. 97 | * 98 | * @param {Function} provider Function to invoke via `$injector.invoke` 99 | * @param {Object} params Parameters for the template. 100 | * @param {Object} locals Locals to pass to `invoke`. Defaults to 101 | * `{ params: params }`. 102 | * @return {string|Promise.} The template html as a string, or a promise 103 | * for that string. 104 | */ 105 | this.fromProvider = function (provider, params, locals) { 106 | return $injector.invoke(provider, null, locals || { params: params }); 107 | }; 108 | } 109 | 110 | angular.module('ui.router.util').service('$templateFactory', $TemplateFactory); 111 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/example/public/components/ui-router/src/view.js: -------------------------------------------------------------------------------- 1 | 2 | $ViewProvider.$inject = []; 3 | function $ViewProvider() { 4 | 5 | this.$get = $get; 6 | /** 7 | * @ngdoc object 8 | * @name ui.router.state.$view 9 | * 10 | * @requires ui.router.util.$templateFactory 11 | * @requires $rootScope 12 | * 13 | * @description 14 | * 15 | */ 16 | $get.$inject = ['$rootScope', '$templateFactory']; 17 | function $get( $rootScope, $templateFactory) { 18 | return { 19 | // $view.load('full.viewName', { template: ..., controller: ..., resolve: ..., async: false, params: ... }) 20 | /** 21 | * @ngdoc function 22 | * @name ui.router.state.$view#load 23 | * @methodOf ui.router.state.$view 24 | * 25 | * @description 26 | * 27 | * @param {string} name name 28 | * @param {object} options option object. 29 | */ 30 | load: function load(name, options) { 31 | var result, defaults = { 32 | template: null, controller: null, view: null, locals: null, notify: true, async: true, params: {} 33 | }; 34 | options = extend(defaults, options); 35 | 36 | if (options.view) { 37 | result = $templateFactory.fromConfig(options.view, options.params, options.locals); 38 | } 39 | if (result && options.notify) { 40 | /** 41 | * @ngdoc event 42 | * @name ui.router.state.$state#$viewContentLoading 43 | * @eventOf ui.router.state.$view 44 | * @eventType broadcast on root scope 45 | * @description 46 | * 47 | * Fired once the view **begins loading**, *before* the DOM is rendered. 48 | * 49 | * @param {Object} event Event object. 50 | * @param {Object} viewConfig The view config properties (template, controller, etc). 51 | * 52 | * @example 53 | * 54 | *
55 |          * $scope.$on('$viewContentLoading',
56 |          * function(event, viewConfig){
57 |          *     // Access to all the view config properties.
58 |          *     // and one special property 'targetView'
59 |          *     // viewConfig.targetView
60 |          * });
61 |          * 
62 | */ 63 | $rootScope.$broadcast('$viewContentLoading', options); 64 | } 65 | return result; 66 | } 67 | }; 68 | } 69 | } 70 | 71 | angular.module('ui.router.state').provider('$view', $ViewProvider); 72 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/example/public/components/ui-router/src/viewDirective.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @ngdoc directive 3 | * @name ui.router.state.directive:ui-view 4 | * 5 | * @requires ui.router.state.$state 6 | * @requires $compile 7 | * @requires $controller 8 | * @requires $injector 9 | * @requires ui.router.state.$uiViewScroll 10 | * @requires $document 11 | * 12 | * @restrict ECA 13 | * 14 | * @description 15 | * The ui-view directive tells $state where to place your templates. 16 | * 17 | * @param {string=} ui-view A view name. The name should be unique amongst the other views in the 18 | * same state. You can have views of the same name that live in different states. 19 | * 20 | * @param {string=} autoscroll It allows you to set the scroll behavior of the browser window 21 | * when a view is populated. By default, $anchorScroll is overridden by ui-router's custom scroll 22 | * service, {@link ui.router.state.$uiViewScroll}. This custom service let's you 23 | * scroll ui-view elements into view when they are populated during a state activation. 24 | * 25 | * *Note: To revert back to old [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll) 26 | * functionality, call `$uiViewScrollProvider.useAnchorScroll()`.* 27 | * 28 | * @param {string=} onload Expression to evaluate whenever the view updates. 29 | * 30 | * @example 31 | * A view can be unnamed or named. 32 | *
 33 |  * 
 34 |  * 
35 | * 36 | * 37 | *
38 | *
39 | * 40 | * You can only have one unnamed view within any template (or root html). If you are only using a 41 | * single view and it is unnamed then you can populate it like so: 42 | *
 43 |  * 
44 | * $stateProvider.state("home", { 45 | * template: "

HELLO!

" 46 | * }) 47 | *
48 | * 49 | * The above is a convenient shortcut equivalent to specifying your view explicitly with the {@link ui.router.state.$stateProvider#views `views`} 50 | * config property, by name, in this case an empty name: 51 | *
 52 |  * $stateProvider.state("home", {
 53 |  *   views: {
 54 |  *     "": {
 55 |  *       template: "

HELLO!

" 56 | * } 57 | * } 58 | * }) 59 | *
60 | * 61 | * But typically you'll only use the views property if you name your view or have more than one view 62 | * in the same template. There's not really a compelling reason to name a view if its the only one, 63 | * but you could if you wanted, like so: 64 | *
 65 |  * 
66 | *
67 | *
 68 |  * $stateProvider.state("home", {
 69 |  *   views: {
 70 |  *     "main": {
 71 |  *       template: "

HELLO!

" 72 | * } 73 | * } 74 | * }) 75 | *
76 | * 77 | * Really though, you'll use views to set up multiple views: 78 | *
 79 |  * 
80 | *
81 | *
82 | *
83 | * 84 | *
 85 |  * $stateProvider.state("home", {
 86 |  *   views: {
 87 |  *     "": {
 88 |  *       template: "

HELLO!

" 89 | * }, 90 | * "chart": { 91 | * template: "" 92 | * }, 93 | * "data": { 94 | * template: "" 95 | * } 96 | * } 97 | * }) 98 | *
99 | * 100 | * Examples for `autoscroll`: 101 | * 102 | *
103 |  * 
105 |  * 
106 |  *
107 |  * 
109 |  * 
110 |  * 
111 |  * 
112 |  * 
113 | */ 114 | $ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll']; 115 | function $ViewDirective( $state, $injector, $uiViewScroll) { 116 | 117 | function getService() { 118 | return ($injector.has) ? function(service) { 119 | return $injector.has(service) ? $injector.get(service) : null; 120 | } : function(service) { 121 | try { 122 | return $injector.get(service); 123 | } catch (e) { 124 | return null; 125 | } 126 | }; 127 | } 128 | 129 | var service = getService(), 130 | $animator = service('$animator'), 131 | $animate = service('$animate'); 132 | 133 | // Returns a set of DOM manipulation functions based on which Angular version 134 | // it should use 135 | function getRenderer(attrs, scope) { 136 | var statics = function() { 137 | return { 138 | enter: function (element, target, cb) { target.after(element); cb(); }, 139 | leave: function (element, cb) { element.remove(); cb(); } 140 | }; 141 | }; 142 | 143 | if ($animate) { 144 | return { 145 | enter: function(element, target, cb) { $animate.enter(element, null, target, cb); }, 146 | leave: function(element, cb) { $animate.leave(element, cb); } 147 | }; 148 | } 149 | 150 | if ($animator) { 151 | var animate = $animator && $animator(scope, attrs); 152 | 153 | return { 154 | enter: function(element, target, cb) {animate.enter(element, null, target); cb(); }, 155 | leave: function(element, cb) { animate.leave(element); cb(); } 156 | }; 157 | } 158 | 159 | return statics(); 160 | } 161 | 162 | var directive = { 163 | restrict: 'ECA', 164 | terminal: true, 165 | priority: 400, 166 | transclude: 'element', 167 | compile: function (tElement, tAttrs, $transclude) { 168 | return function (scope, $element, attrs) { 169 | var previousEl, currentEl, currentScope, latestLocals, 170 | onloadExp = attrs.onload || '', 171 | autoScrollExp = attrs.autoscroll, 172 | renderer = getRenderer(attrs, scope); 173 | 174 | scope.$on('$stateChangeSuccess', function() { 175 | updateView(false); 176 | }); 177 | scope.$on('$viewContentLoading', function() { 178 | updateView(false); 179 | }); 180 | 181 | updateView(true); 182 | 183 | function cleanupLastView() { 184 | if (previousEl) { 185 | previousEl.remove(); 186 | previousEl = null; 187 | } 188 | 189 | if (currentScope) { 190 | currentScope.$destroy(); 191 | currentScope = null; 192 | } 193 | 194 | if (currentEl) { 195 | renderer.leave(currentEl, function() { 196 | previousEl = null; 197 | }); 198 | 199 | previousEl = currentEl; 200 | currentEl = null; 201 | } 202 | } 203 | 204 | function updateView(firstTime) { 205 | var newScope, 206 | name = getUiViewName(attrs, $element.inheritedData('$uiView')), 207 | previousLocals = name && $state.$current && $state.$current.locals[name]; 208 | 209 | if (!firstTime && previousLocals === latestLocals) return; // nothing to do 210 | newScope = scope.$new(); 211 | latestLocals = $state.$current.locals[name]; 212 | 213 | var clone = $transclude(newScope, function(clone) { 214 | renderer.enter(clone, $element, function onUiViewEnter() { 215 | if (angular.isDefined(autoScrollExp) && !autoScrollExp || scope.$eval(autoScrollExp)) { 216 | $uiViewScroll(clone); 217 | } 218 | }); 219 | cleanupLastView(); 220 | }); 221 | 222 | currentEl = clone; 223 | currentScope = newScope; 224 | /** 225 | * @ngdoc event 226 | * @name ui.router.state.directive:ui-view#$viewContentLoaded 227 | * @eventOf ui.router.state.directive:ui-view 228 | * @eventType emits on ui-view directive scope 229 | * @description * 230 | * Fired once the view is **loaded**, *after* the DOM is rendered. 231 | * 232 | * @param {Object} event Event object. 233 | */ 234 | currentScope.$emit('$viewContentLoaded'); 235 | currentScope.$eval(onloadExp); 236 | } 237 | }; 238 | } 239 | }; 240 | 241 | return directive; 242 | } 243 | 244 | $ViewDirectiveFill.$inject = ['$compile', '$controller', '$state']; 245 | function $ViewDirectiveFill ($compile, $controller, $state) { 246 | return { 247 | restrict: 'ECA', 248 | priority: -400, 249 | compile: function (tElement) { 250 | var initial = tElement.html(); 251 | return function (scope, $element, attrs) { 252 | var current = $state.$current, 253 | name = getUiViewName(attrs, $element.inheritedData('$uiView')), 254 | locals = current && current.locals[name]; 255 | 256 | if (! locals) { 257 | return; 258 | } 259 | 260 | $element.data('$uiView', { name: name, state: locals.$$state }); 261 | $element.html(locals.$template ? locals.$template : initial); 262 | 263 | var link = $compile($element.contents()); 264 | 265 | if (locals.$$controller) { 266 | locals.$scope = scope; 267 | var controller = $controller(locals.$$controller, locals); 268 | if (locals.$$controllerAs) { 269 | scope[locals.$$controllerAs] = controller; 270 | } 271 | $element.data('$ngControllerController', controller); 272 | $element.children().data('$ngControllerController', controller); 273 | } 274 | 275 | link(scope); 276 | }; 277 | } 278 | }; 279 | } 280 | 281 | /** 282 | * Shared ui-view code for both directives: 283 | * Given attributes and inherited $uiView data, return the view's name 284 | */ 285 | function getUiViewName(attrs, inherited) { 286 | var name = attrs.uiView || attrs.name || ''; 287 | return name.indexOf('@') >= 0 ? name : (name + '@' + (inherited ? inherited.state.name : '')); 288 | } 289 | 290 | angular.module('ui.router.state').directive('uiView', $ViewDirective); 291 | angular.module('ui.router.state').directive('uiView', $ViewDirectiveFill); 292 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/example/public/components/ui-router/src/viewScroll.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @ngdoc object 3 | * @name ui.router.state.$uiViewScrollProvider 4 | * 5 | * @description 6 | * Provider that returns the {@link ui.router.state.$uiViewScroll} service function. 7 | */ 8 | function $ViewScrollProvider() { 9 | 10 | var useAnchorScroll = false; 11 | 12 | /** 13 | * @ngdoc function 14 | * @name ui.router.state.$uiViewScrollProvider#useAnchorScroll 15 | * @methodOf ui.router.state.$uiViewScrollProvider 16 | * 17 | * @description 18 | * Reverts back to using the core [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll) service for 19 | * scrolling based on the url anchor. 20 | */ 21 | this.useAnchorScroll = function () { 22 | useAnchorScroll = true; 23 | }; 24 | 25 | /** 26 | * @ngdoc object 27 | * @name ui.router.state.$uiViewScroll 28 | * 29 | * @requires $anchorScroll 30 | * @requires $timeout 31 | * 32 | * @description 33 | * When called with a jqLite element, it scrolls the element into view (after a 34 | * `$timeout` so the DOM has time to refresh). 35 | * 36 | * If you prefer to rely on `$anchorScroll` to scroll the view to the anchor, 37 | * this can be enabled by calling {@link ui.router.state.$uiViewScrollProvider#methods_useAnchorScroll `$uiViewScrollProvider.useAnchorScroll()`}. 38 | */ 39 | this.$get = ['$anchorScroll', '$timeout', function ($anchorScroll, $timeout) { 40 | if (useAnchorScroll) { 41 | return $anchorScroll; 42 | } 43 | 44 | return function ($element) { 45 | $timeout(function () { 46 | $element[0].scrollIntoView(); 47 | }, 0, false); 48 | }; 49 | }]; 50 | } 51 | 52 | angular.module('ui.router.state').provider('$uiViewScroll', $ViewScrollProvider); 53 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/example/public/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Helvetica, Verdana; 3 | margin: 0; 4 | } 5 | 6 | textarea { 7 | padding: 100px; 8 | border: none; 9 | width: 100%; 10 | position: absolute; 11 | bottom: 0; 12 | top: 0; 13 | font-size: 24px; 14 | -webkit-box-sizing: border-box; 15 | -moz-box-sizing: border-box; 16 | box-sizing: border-box; 17 | } 18 | 19 | *:focus { 20 | outline: none; 21 | } 22 | 23 | #setup-area { 24 | text-align: center; 25 | } 26 | 27 | #username { 28 | width: 200px; 29 | height: 30px; 30 | font-size: 24px; 31 | border: none; 32 | border-bottom: 3px solid #df4e69; 33 | font-weight: light; 34 | margin-top: 100px; 35 | } 36 | 37 | .channel-number { 38 | border-radius: 50%; 39 | width: 50px; 40 | height: 50px; 41 | text-align: center; 42 | line-height: 50px; 43 | font-size: 28px; 44 | background-color: #df4e69; 45 | color: white; 46 | border: 3px solid white; 47 | } 48 | 49 | .channel-number:hover { 50 | border: 3px solid #df4e69; 51 | color: #df4e69; 52 | background: white; 53 | cursor: pointer; 54 | } 55 | 56 | .choose-text { 57 | font-size: 24px; 58 | margin-top: 80px; 59 | } 60 | 61 | .channel-chooser { 62 | margin: auto; 63 | margin-top: 30px; 64 | width: 400px; 65 | display: flex; 66 | justify-content: space-between; 67 | } -------------------------------------------------------------------------------- /example/public/components/pusher-angular/example/public/js/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('pusherCollaboration', [ 4 | 'pusherCollabControllers', 5 | 'pusherCollabDirectives', 6 | 'pusherCollabServices', 7 | 'ui.router', 8 | 'pusher-angular' 9 | ]) 10 | .config(['$urlRouterProvider', '$locationProvider', '$stateProvider', 11 | function($urlRouterProvider, $locationProvider, $stateProvider) { 12 | $urlRouterProvider.otherwise('/'); 13 | 14 | $stateProvider 15 | .state('home', { 16 | url: '/', 17 | templateUrl: '/views/setup.html', 18 | controller: 'SetupCtrl' 19 | }) 20 | .state('collaborate', { 21 | url: '/collaborate', 22 | templateUrl: '/views/text.html', 23 | controller: 'CollaborationCtrl' 24 | }) 25 | } 26 | ]); 27 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/example/public/js/controllers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var pCControllers = angular.module('pusherCollabControllers', ['pusherCollabServices']); 4 | 5 | pCControllers.controller('CollaborationCtrl', [ 6 | '$scope', 7 | '$pusher', 8 | '$http', 9 | '$state', 10 | 'channelManager', 11 | function ($scope, $pusher, $http, $state, channelManager) { 12 | var channel; 13 | var textzone = document.getElementById('textzone'); 14 | 15 | $scope.members = channelManager.getMembers(); 16 | 17 | $scope.sendUpdate = function (event) { 18 | channel = channelManager.get(); 19 | var num = channelManager.getChannelNum(); 20 | if (channel) { 21 | $http.post('/text-update', { channelNum: num, text: event.srcElement.value, socket_id: channel.client.connection.baseConnection.socket_id }); 22 | } 23 | } 24 | 25 | $scope.$watch('text', function () { 26 | var r = 0; 27 | var temp = $scope.text.replace(/\s/g,' '); 28 | temp = temp.split(' '); 29 | for (var i = 0; i < temp.length; i++) { 30 | if (temp[i].length > 0) 31 | r++; 32 | }; 33 | 34 | $scope.words = r; 35 | $scope.characters = $scope.text.length; 36 | }); 37 | 38 | $scope.paragraphs = 0; 39 | $scope.words = 0; 40 | $scope.characters = 0; 41 | $scope.allCharacters = 0; 42 | } 43 | ]); 44 | 45 | pCControllers.controller('SetupCtrl', [ 46 | '$scope', 47 | '$pusher', 48 | '$http', 49 | '$state', 50 | 'channelManager', 51 | function ($scope, $pusher, $http, $state, channelManager) { 52 | $scope.channel1ReadOnly = false; 53 | $scope.channel2ReadOnly = false; 54 | $scope.channel3ReadOnly = false; 55 | 56 | 57 | $scope.$watch('channel1ReadOnly', function() { 58 | if ($scope.channel1ReadOnly) { 59 | channelManager.set(1, $scope.username); 60 | $state.go('collaborate'); 61 | } 62 | }) 63 | 64 | $scope.$watch('channel2ReadOnly', function() { 65 | if ($scope.channel2ReadOnly) { 66 | channelManager.set(2, $scope.username); 67 | $state.go('collaborate'); 68 | } 69 | }) 70 | 71 | $scope.$watch('channel3ReadOnly', function() { 72 | if ($scope.channel3ReadOnly) { 73 | channelManager.set(3, $scope.username); 74 | $state.go('collaborate'); 75 | } 76 | }) 77 | } 78 | ]); 79 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/example/public/js/directives.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var pCDirectives = angular.module('pusherCollabDirectives', ['pusherCollabServices']); 4 | 5 | pCDirectives.directive('collaborate', ['$pusher', 'channelManager', function($pusher, channelManager) { 6 | var linker = function(scope, elem, attrs) { 7 | scope.text = ''; 8 | 9 | var channel = channelManager.get(); 10 | 11 | function sortNumber(a, b) { 12 | return a - b; 13 | } 14 | 15 | channel.bind('pusher:subscription_succeeded', function (members) { 16 | if (channel.members.count > 1) { 17 | channel.trigger('client-joined', channel.members.me.id); 18 | } 19 | }); 20 | 21 | channel.bind('client-joined', function (id) { 22 | var ids = []; 23 | for (var member in channel.members.members) { 24 | ids.push(member); 25 | } 26 | if (ids.indexOf(id) !== -1) { ids.splice(ids.indexOf(id), 1); } 27 | if (channel.members.me.id !== ids.sort(sortNumber)[0]) { 28 | channel.trigger('client-catchup', { id: id, text: scope.text }); 29 | } 30 | }); 31 | 32 | channel.bind('client-catchup', function (data) { 33 | if (data.id == channel.members.me.id) { 34 | scope.text = data.text; 35 | } 36 | }); 37 | 38 | if (channel) { 39 | channel.bind('update', function (text) { 40 | if (elem.value !== text) { 41 | scope.text = text; 42 | } 43 | }); 44 | } 45 | } 46 | return { 47 | restrict: 'A', 48 | link: linker 49 | } 50 | }]); 51 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/example/public/js/services.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var pCServices = angular.module('pusherCollabServices', []); 4 | 5 | pCDirectives.factory('channelManager', ['$pusher', function($pusher) { 6 | var channel, cNum; 7 | 8 | return { 9 | get: function () { 10 | return channel; 11 | }, 12 | set: function (channelNum, username) { 13 | cNum = channelNum; 14 | var client = new Pusher('YOUR_APP_KEY', { auth: { params: { username: username}} }); 15 | var pusher = $pusher(client); 16 | channel = pusher.subscribe('presence-collaborate-' + channelNum); 17 | return channel; 18 | }, 19 | getChannelNum: function () { 20 | return cNum; 21 | }, 22 | getMembers: function () { 23 | return channel.members; 24 | } 25 | } 26 | }]); 27 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/example/public/views/setup.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 |
Choose a channel
6 |
7 |
1
8 |
2
9 |
3
10 |
11 | 12 |
13 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/example/public/views/text.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 | Me: {{members.me.info.username}}    6 | Members ({{members.count}}): 7 | {{member.username}}{{$last ? '' : ', '}} 8 | 9 |
10 |
11 | 12 | Words: {{words}} 13 | Characters: {{characters}} 14 |
15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/example/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var express = require('express'); 4 | var app = express(); 5 | var bodyParser = require('body-parser'); 6 | var Pusher = require('pusher'); 7 | 8 | app.set('views', __dirname + '/app/views'); 9 | app.set('view engine', 'ejs'); 10 | 11 | var pusherConfig = {}; 12 | try { 13 | pusherConfig = require('./pusherConfig'); 14 | } catch (err) { 15 | pusherConfig.appId = 'YOUR_APP_ID'; 16 | pusherConfig.key = 'YOUR_APP_KEY'; 17 | pusherConfig.secret = 'YOUR_APP_SECRET'; 18 | } 19 | 20 | var pusher = new Pusher(pusherConfig); 21 | var port = Number(process.env.PORT || 3000); 22 | var host = 'localhost'; 23 | 24 | app.use(bodyParser()); 25 | app.use(express.static(__dirname + '/public')); 26 | 27 | app.post('/text-update', function(req, res) { 28 | pusher.trigger('presence-collaborate-' + req.body.channelNum, 'update', req.body.text, req.body.socket_id); 29 | res.send(); 30 | }); 31 | 32 | app.post('/pusher/auth', function(req, res) { 33 | var socketId = req.body.socket_id; 34 | var channel = req.body.channel_name; 35 | var auth = pusher.authenticate( socketId, channel, { 36 | user_id: "" + Math.floor(Math.random() * (1000 - 1) + 1), 37 | user_info: { username: req.body.username } 38 | }); 39 | res.send( auth ); 40 | }); 41 | 42 | app.get('*', function(req, res){ 43 | res.render('index'); 44 | }); 45 | 46 | app.listen(port); 47 | 48 | console.log('Server running at http://'+host+':'+port.toString()+'/'); -------------------------------------------------------------------------------- /example/public/components/pusher-angular/lib/pusher-angular.min.js: -------------------------------------------------------------------------------- 1 | "use strict";angular.module("pusher-angular",[]).factory("$pusher",["$rootScope","$channel","$connection",function(n,e,t){function i(n){if(!(this instanceof i)){return new i(n)}this._assertValidClient(n);this.client=n;this.connection=t(n.connection,n);this.channels={}}i.prototype={subscribe:function(n){var t=this.client.channel(n);if(t===undefined){t=this.client.subscribe(n)}t=e(t,this);this.channels[n]=t;return t},unsubscribe:function(n){if(this.client.channel(n)){this.client.unsubscribe(n);if(this.channels[n]){delete this.channels[n]}}},bind:function(e,t){this.client.bind(e,function(e){t(e);n.$digest()})},bind_all:function(e){this.client.bind_all(function(t,i){e(t,i);n.$digest()})},unbind:function(n,e){this.client.unbind(n,e)},disconnect:function(){this.client.disconnect()},channel:function(n){return this.channels[n]},allChannels:function(){return this.channels},_assertValidClient:function(n){if(!angular.isObject(n)||!angular.isObject(n.connection)||typeof n.channel!=="function"){throw new Error("Invalid Pusher client object")}}};return i}]).factory("$channel",["$rootScope","$members",function(n,e){function t(n){if(n.indexOf("presence-")==-1&&n.indexOf("private-")==-1){throw new Error("Presence or private channel required")}}function i(n,t){if(!(this instanceof i)){return new i(n,t)}this._assertValidChannel(n);this.baseChannel=n;this.client=t;this.name=n.name;if(n.name.indexOf("presence")==-1){this.members=function(){throw new Error("Members object only exists for presence channels")}}else{this.members=e(n.members,n)}}i.prototype={bind:function(e,t,i){this.baseChannel.bind(e,function(e){t(e);n.$digest()},i)},unbind:function(n,e,t){this.baseChannel.unbind(n,e,t)},bind_all:function(e){this.baseChannel.bind_all(function(t,i){e(t,i);n.$digest()})},trigger:function(n,e){t(this.name);if(n.indexOf("client-")==-1){throw new Error("Event name requires 'client-' prefix")}return this.baseChannel.trigger(n,e)},_assertValidChannel:function(n){if(!angular.isObject(n)||typeof n.name!=="string"){throw new Error("Invalid Pusher channel object")}}};return i}]).factory("$members",["$rootScope",function(n){function e(t,i){if(!(this instanceof e)){return new e(t,i)}var s=this;this._assertValidMembers(t);this.baseMembers=t;this.baseChannel=i;this.me={};this.count=0;this.members={};i.bind("pusher:subscription_succeeded",function(e){s.me=e.me;s.count=e.count;s.members=e.members;n.$digest()});i.bind("pusher:member_added",function(e){s.count++;if(e.info){s.members[e.id.toString()]=e.info}else{s.members[e.id.toString()]=null}n.$digest()});i.bind("pusher:member_removed",function(e){s.count--;delete s.members[e.id.toString()];n.$digest()})}e.prototype={get:function(n){return this.baseMembers.get(n)},each:function(e){this.baseMembers.each(function(t){e(t);n.$digest()})},_assertValidMembers:function(n){if(!angular.isObject(n)||typeof n.me!=="object"){throw new Error("Invalid Pusher channel members object")}}};return e}]).factory("$connection",["$rootScope",function(n){function e(n,t){if(!(this instanceof e)){return new e(n,t)}this._assertValidConnection(n);this.baseConnection=n;this.baseClient=t}e.prototype={bind:function(e,t,i){this.baseConnection.bind(e,function(e){t(e);n.$digest()},i)},bind_all:function(e){this.baseConnection.bind_all(function(t,i){e(t,i);n.$digest()})},_assertValidConnection:function(n){if(!angular.isObject(n)){throw new Error("Invalid Pusher connection object")}}};return e}]); -------------------------------------------------------------------------------- /example/public/components/pusher-angular/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pusher-angular", 3 | "version": "0.1.7", 4 | "description": "Angular library for using Pusher", 5 | "main": "lib/pusher-angular.js", 6 | "scripts": { 7 | "test": "karma start travis_karma.conf.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/pusher/pusher-angular.git" 12 | }, 13 | "keywords": [ 14 | "pusher", 15 | "angular", 16 | "websockets" 17 | ], 18 | "author": "Hamilton Chapman", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/pusher/pusher-angular/issues" 22 | }, 23 | "homepage": "https://github.com/pusher/pusher-angular", 24 | "devDependencies": { 25 | "coveralls": "^2.11.2", 26 | "istanbul": "^0.3.2", 27 | "karma": "^0.12.24", 28 | "karma-chrome-launcher": "^0.1.5", 29 | "karma-cli": "0.0.4", 30 | "karma-coverage": "^0.2.6", 31 | "karma-firefox-launcher": "^0.1.3", 32 | "karma-jasmine": "^0.2.2", 33 | "karma-phantomjs-launcher": "^0.1.4", 34 | "karma-spec-reporter": "0.0.13", 35 | "uglify-js": "latest" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/public/components/angular-mocks/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-mocks", 3 | "version": "1.3.15", 4 | "main": "./angular-mocks.js", 5 | "ignore": [], 6 | "dependencies": { 7 | "angular": "1.3.15" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/public/components/angular-mocks/ngAnimateMock.js: -------------------------------------------------------------------------------- 1 | require('./angular-mocks'); 2 | module.exports = 'ngAnimateMock'; 3 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/public/components/angular-mocks/ngMock.js: -------------------------------------------------------------------------------- 1 | require('./angular-mocks'); 2 | module.exports = 'ngMock'; 3 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/public/components/angular-mocks/ngMockE2E.js: -------------------------------------------------------------------------------- 1 | require('./angular-mocks'); 2 | module.exports = 'ngMockE2E'; 3 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/public/components/angular-mocks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-mocks", 3 | "version": "1.3.15", 4 | "description": "AngularJS mocks for testing", 5 | "main": "angular-mocks.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/angular/angular.js.git" 12 | }, 13 | "keywords": [ 14 | "angular", 15 | "framework", 16 | "browser", 17 | "mocks", 18 | "testing", 19 | "client-side" 20 | ], 21 | "author": "Angular Core Team ", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/angular/angular.js/issues" 25 | }, 26 | "homepage": "http://angularjs.org" 27 | } 28 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/public/components/angular/angular-csp.css: -------------------------------------------------------------------------------- 1 | /* Include this file in your html if you are using the CSP mode. */ 2 | 3 | @charset "UTF-8"; 4 | 5 | [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], 6 | .ng-cloak, .x-ng-cloak, 7 | .ng-hide:not(.ng-hide-animate) { 8 | display: none !important; 9 | } 10 | 11 | ng\:form { 12 | display: block; 13 | } 14 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/public/components/angular/angular.min.js.gzip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pusher/pusher-angular/26f48e0ed23e40f63cb8faa8b7a8bc70a716d95a/example/public/components/pusher-angular/public/components/angular/angular.min.js.gzip -------------------------------------------------------------------------------- /example/public/components/pusher-angular/public/components/angular/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular", 3 | "version": "1.3.15", 4 | "main": "./angular.js", 5 | "ignore": [], 6 | "dependencies": { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/public/components/angular/index.js: -------------------------------------------------------------------------------- 1 | require('./angular'); 2 | module.exports = angular; 3 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/public/components/angular/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular", 3 | "version": "1.3.15", 4 | "description": "HTML enhanced for web apps", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/angular/angular.js.git" 12 | }, 13 | "keywords": [ 14 | "angular", 15 | "framework", 16 | "browser", 17 | "client-side" 18 | ], 19 | "author": "Angular Core Team ", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/angular/angular.js/issues" 23 | }, 24 | "homepage": "http://angularjs.org" 25 | } 26 | -------------------------------------------------------------------------------- /example/public/components/pusher-angular/travis_karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Wed Oct 01 2014 14:31:47 GMT+0100 (BST) 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | 7 | // base path that will be used to resolve all patterns (eg. files, exclude) 8 | basePath: '', 9 | 10 | 11 | // frameworks to use 12 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 13 | frameworks: ['jasmine'], 14 | 15 | 16 | // list of files / patterns to load in the browser 17 | files: [ 18 | "public/components/angular/angular.js", 19 | "public/components/angular-mocks/angular-mocks.js", 20 | "tests/specs.js", 21 | "lib/pusher-angular.js" 22 | ], 23 | 24 | 25 | // list of files to exclude 26 | exclude: [ 27 | ], 28 | 29 | 30 | // preprocess matching files before serving them to the browser 31 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 32 | preprocessors: { 33 | 'lib/pusher-angular.js': 'coverage' 34 | }, 35 | 36 | // test results reporter to use 37 | // possible values: 'dots', 'progress' 38 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 39 | reporters: ['progress', 'spec', 'coverage'], 40 | 41 | coverageReporter: { 42 | reporters: [ 43 | { 44 | type : 'text-summary' 45 | }, 46 | { 47 | type: "lcovonly", 48 | dir: "coverage", 49 | subdir: "." 50 | } 51 | ] 52 | }, 53 | 54 | 55 | // web server port 56 | port: 9876, 57 | 58 | 59 | // enable / disable colors in the output (reporters and logs) 60 | colors: true, 61 | 62 | 63 | // level of logging 64 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 65 | logLevel: config.LOG_INFO, 66 | 67 | 68 | // enable / disable watching file and executing tests whenever any file changes 69 | autoWatch: false, 70 | 71 | 72 | // start these browsers 73 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 74 | // browsers: ['Chrome', 'Firefox', 'Safari', 'Opera'], 75 | browsers: ['Firefox'], 76 | 77 | 78 | // Continuous Integration mode 79 | // if true, Karma captures browsers, runs the tests and exits 80 | singleRun: true 81 | }); 82 | }; 83 | -------------------------------------------------------------------------------- /example/public/components/ui-router/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-ui-router", 3 | "version": "0.2.11", 4 | "main": "./release/angular-ui-router.js", 5 | "dependencies": { 6 | "angular": ">= 1.0.8" 7 | }, 8 | "ignore": [ 9 | "**/.*", 10 | "node_modules", 11 | "bower_components", 12 | "component.json", 13 | "package.json", 14 | "lib", 15 | "config", 16 | "sample", 17 | "test", 18 | "tests", 19 | "ngdoc_assets", 20 | "Gruntfile.js", 21 | "files.js" 22 | ], 23 | "homepage": "https://github.com/angular-ui/ui-router", 24 | "_release": "0.2.11", 25 | "_resolution": { 26 | "type": "version", 27 | "tag": "0.2.11", 28 | "commit": "8ee633f45dfac9f809344edd0c89d6e018887e4f" 29 | }, 30 | "_source": "git://github.com/angular-ui/ui-router.git", 31 | "_target": "~0.2.11", 32 | "_originalSource": "ui-router", 33 | "_direct": true 34 | } -------------------------------------------------------------------------------- /example/public/components/ui-router/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | ### 0.2.11 (2014-08-26) 3 | 4 | 5 | #### Bug Fixes 6 | 7 | * **$resolve:** Resolves only inherit from immediate parent fixes #702 ([df34e20c](https://github.com/angular-ui/ui-router/commit/df34e20c576299e7a3c8bd4ebc68d42341c0ace9)) 8 | * **$state:** 9 | * change $state.href default options.inherit to true ([deea695f](https://github.com/angular-ui/ui-router/commit/deea695f5cacc55de351ab985144fd233c02a769)) 10 | * sanity-check state lookups ([456fd5ae](https://github.com/angular-ui/ui-router/commit/456fd5aec9ea507518927bfabd62b4afad4cf714), closes [#980](https://github.com/angular-ui/ui-router/issues/980)) 11 | * didn't comply to inherit parameter ([09836781](https://github.com/angular-ui/ui-router/commit/09836781f126c1c485b06551eb9cfd4fa0f45c35)) 12 | * allow view content loading broadcast ([7b78edee](https://github.com/angular-ui/ui-router/commit/7b78edeeb52a74abf4d3f00f79534033d5a08d1a)) 13 | * **$urlMatcherFactory:** 14 | * detect injected functions ([91f75ae6](https://github.com/angular-ui/ui-router/commit/91f75ae66c4d129f6f69e53bd547594e9661f5d5)) 15 | * syntax ([1ebed370](https://github.com/angular-ui/ui-router/commit/1ebed37069bae8614d41541d56521f5c45f703f3)) 16 | * **UrlMatcher:** 17 | * query param function defaults ([f9c20530](https://github.com/angular-ui/ui-router/commit/f9c205304f10d8a4ebe7efe9025e642016479a51)) 18 | * don't decode default values ([63607bdb](https://github.com/angular-ui/ui-router/commit/63607bdbbcb432d3fb37856a1cb3da0cd496804e)) 19 | * **travis:** update Node version to fix build ([d6b95ef2](https://github.com/angular-ui/ui-router/commit/d6b95ef23d9dacb4eba08897f5190a0bcddb3a48)) 20 | * **uiSref:** 21 | * Generate an href for states with a blank url. closes #1293 ([691745b1](https://github.com/angular-ui/ui-router/commit/691745b12fa05d3700dd28f0c8d25f8a105074ad)) 22 | * should inherit params by default ([b973dad1](https://github.com/angular-ui/ui-router/commit/b973dad155ad09a7975e1476bd096f7b2c758eeb)) 23 | * cancel transition if preventDefault() has been called ([2e6d9167](https://github.com/angular-ui/ui-router/commit/2e6d9167d3afbfbca6427e53e012f94fb5fb8022)) 24 | * **uiView:** Fixed infinite loop when is called .go() from a controller. ([e13988b8](https://github.com/angular-ui/ui-router/commit/e13988b8cd6231d75c78876ee9d012cc87f4a8d9), closes [#1194](https://github.com/angular-ui/ui-router/issues/1194)) 25 | * **docs:** 26 | * Fixed link to milestones ([6c0ae500](https://github.com/angular-ui/ui-router/commit/6c0ae500cc238ea9fc95adcc15415c55fc9e1f33)) 27 | * fix bug in decorator example ([4bd00af5](https://github.com/angular-ui/ui-router/commit/4bd00af50b8b88a49d1545a76290731cb8e0feb1)) 28 | * Removed an incorrect semi-colon ([af97cef8](https://github.com/angular-ui/ui-router/commit/af97cef8b967f2e32177e539ef41450dca131a7d)) 29 | * Explain return value of rule as function ([5e887890](https://github.com/angular-ui/ui-router/commit/5e8878900a6ffe59a81aed531a3925e34a297377)) 30 | 31 | 32 | #### Features 33 | 34 | * **$state:** 35 | * allow parameters to pass unharmed ([8939d057](https://github.com/angular-ui/ui-router/commit/8939d0572ab1316e458ef016317ecff53131a822)) 36 | * allow prevent syncUrl on failure ([753060b9](https://github.com/angular-ui/ui-router/commit/753060b910d5d2da600a6fa0757976e401c33172)) 37 | * **typescript:** Add typescript definitions for component builds ([521ceb3f](https://github.com/angular-ui/ui-router/commit/521ceb3fd7850646422f411921e21ce5e7d82e0f)) 38 | * **uiSref:** extend syntax for ui-sref ([71cad3d6](https://github.com/angular-ui/ui-router/commit/71cad3d636508b5a9fe004775ad1f1adc0c80c3e)) 39 | * **uiSrefActive:** 40 | * **BREAKING CHANGE** Also activate for child states. ([bf163ad6](https://github.com/angular-ui/ui-router/commit/bf163ad6ce176ce28792696c8302d7cdf5c05a01), closes [#818](https://github.com/angular-ui/ui-router/issues/818)) 41 | * uiSrefActiveEq: new directive with old ui-sref-active behavior 42 | * **$urlRouter:** 43 | * defer URL change interception ([c72d8ce1](https://github.com/angular-ui/ui-router/commit/c72d8ce11916d0ac22c81b409c9e61d7048554d7)) 44 | * force URLs to have valid params ([d48505cd](https://github.com/angular-ui/ui-router/commit/d48505cd328d83e39d5706e085ba319715f999a6)) 45 | * abstract $location handling ([08b4636b](https://github.com/angular-ui/ui-router/commit/08b4636b294611f08db35f00641eb5211686fb50)) 46 | * **$urlMatcherFactory:** 47 | * fail on bad parameters ([d8f124c1](https://github.com/angular-ui/ui-router/commit/d8f124c10d00c7e5dde88c602d966db261aea221)) 48 | * date type support ([b7f074ff](https://github.com/angular-ui/ui-router/commit/b7f074ff65ca150a3cdbda4d5ad6cb17107300eb)) 49 | * implement type support ([450b1f0e](https://github.com/angular-ui/ui-router/commit/450b1f0e8e03c738174ff967f688b9a6373290f4)) 50 | * **UrlMatcher:** 51 | * handle query string arrays ([9cf764ef](https://github.com/angular-ui/ui-router/commit/9cf764efab45fa9309368688d535ddf6e96d6449), closes [#373](https://github.com/angular-ui/ui-router/issues/373)) 52 | * injectable functions as defaults ([00966ecd](https://github.com/angular-ui/ui-router/commit/00966ecd91fb745846039160cab707bfca8b3bec)) 53 | * default values & type decoding for query params ([a472b301](https://github.com/angular-ui/ui-router/commit/a472b301389fbe84d1c1fa9f24852b492a569d11)) 54 | * allow shorthand definitions ([5b724304](https://github.com/angular-ui/ui-router/commit/5b7243049793505e44b6608ea09878c37c95b1f5)) 55 | * validates whole interface ([32b27db1](https://github.com/angular-ui/ui-router/commit/32b27db173722e9194ef1d5c0ea7d93f25a98d11)) 56 | * implement non-strict matching ([a3e21366](https://github.com/angular-ui/ui-router/commit/a3e21366bee0475c9795a1ec76f70eec41c5b4e3)) 57 | * add per-param config support ([07b3029f](https://github.com/angular-ui/ui-router/commit/07b3029f4d409cf955780113df92e36401b47580)) 58 | 59 | 60 | ### 0.2.10 (2014-03-12) 61 | 62 | 63 | #### Bug Fixes 64 | 65 | * **$state:** use $browser.baseHref() when generating urls with .href() ([cbcc8488](https://github.com/angular-ui/ui-router/commit/cbcc84887d6b6d35258adabb97c714cd9c1e272d)) 66 | * **bower.json:** JS files should not be ignored ([ccdab193](https://github.com/angular-ui/ui-router/commit/ccdab193315f304eb3be5f5b97c47a926c79263e)) 67 | * **dev:** karma:background task is missing, can't run grunt:dev. ([d9f7b898](https://github.com/angular-ui/ui-router/commit/d9f7b898e8e3abb8c846b0faa16a382913d7b22b)) 68 | * **sample:** Contacts menu button not staying active when navigating to detail states. Need t ([2fcb8443](https://github.com/angular-ui/ui-router/commit/2fcb84437cb43ade12682a92b764f13cac77dfe7)) 69 | * **uiSref:** support mock-clicks/events with no data ([717d3ff7](https://github.com/angular-ui/ui-router/commit/717d3ff7d0ba72d239892dee562b401cdf90e418)) 70 | * **uiView:** 71 | * Do NOT autoscroll when autoscroll attr is missing ([affe5bd7](https://github.com/angular-ui/ui-router/commit/affe5bd785cdc3f02b7a9f64a52e3900386ec3a0), closes [#807](https://github.com/angular-ui/ui-router/issues/807)) 72 | * Refactoring uiView directive to copy ngView logic ([548fab6a](https://github.com/angular-ui/ui-router/commit/548fab6ab9debc9904c5865c8bc68b4fc3271dd0), closes [#857](https://github.com/angular-ui/ui-router/issues/857), [#552](https://github.com/angular-ui/ui-router/issues/552)) 73 | 74 | 75 | #### Features 76 | 77 | * **$state:** includes() allows glob patterns for state matching. ([2d5f6b37](https://github.com/angular-ui/ui-router/commit/2d5f6b37191a3135f4a6d9e8f344c54edcdc065b)) 78 | * **UrlMatcher:** Add support for case insensitive url matching ([642d5247](https://github.com/angular-ui/ui-router/commit/642d524799f604811e680331002feec7199a1fb5)) 79 | * **uiSref:** add support for transition options ([2ed7a728](https://github.com/angular-ui/ui-router/commit/2ed7a728cee6854b38501fbc1df6139d3de5b28a)) 80 | * **uiView:** add controllerAs config with function ([1ee7334a](https://github.com/angular-ui/ui-router/commit/1ee7334a73efeccc9b95340e315cdfd59944762d)) 81 | 82 | 83 | ### 0.2.9 (2014-01-17) 84 | 85 | 86 | This release is identical to 0.2.8. 0.2.8 was re-tagged in git to fix a problem with bower. 87 | 88 | 89 | ### 0.2.8 (2014-01-16) 90 | 91 | 92 | #### Bug Fixes 93 | 94 | * **$state:** allow null to be passed as 'params' param ([094dc30e](https://github.com/angular-ui/ui-router/commit/094dc30e883e1bd14e50a475553bafeaade3b178)) 95 | * **$state.go:** param inheritance shouldn't inherit from siblings ([aea872e0](https://github.com/angular-ui/ui-router/commit/aea872e0b983cb433436ce5875df10c838fccedb)) 96 | * **bower.json:** fixes bower.json ([eed3cc4d](https://github.com/angular-ui/ui-router/commit/eed3cc4d4dfef1d3ef84b9fd063127538ebf59d3)) 97 | * **uiSrefActive:** annotate controller injection ([85921422](https://github.com/angular-ui/ui-router/commit/85921422ff7fb0effed358136426d616cce3d583), closes [#671](https://github.com/angular-ui/ui-router/issues/671)) 98 | * **uiView:** 99 | * autoscroll tests pass on 1.2.4 & 1.1.5 ([86eacac0](https://github.com/angular-ui/ui-router/commit/86eacac09ca5e9000bd3b9c7ba6e2cc95d883a3a)) 100 | * don't animate initial load ([83b6634d](https://github.com/angular-ui/ui-router/commit/83b6634d27942ca74766b2b1244a7fc52c5643d9)) 101 | * test pass against 1.0.8 and 1.2.4 ([a402415a](https://github.com/angular-ui/ui-router/commit/a402415a2a28b360c43b9fe8f4f54c540f6c33de)) 102 | * it should autoscroll when expr is missing. ([8bb9e27a](https://github.com/angular-ui/ui-router/commit/8bb9e27a2986725f45daf44c4c9f846385095aff)) 103 | 104 | 105 | #### Features 106 | 107 | * **uiSref:** add target attribute behaviour ([c12bf9a5](https://github.com/angular-ui/ui-router/commit/c12bf9a520d30d70294e3d82de7661900f8e394e)) 108 | * **uiView:** 109 | * merge autoscroll expression test. ([b89e0f87](https://github.com/angular-ui/ui-router/commit/b89e0f871d5cc35c10925ede986c10684d5c9252)) 110 | * cache and test autoscroll expression ([ee262282](https://github.com/angular-ui/ui-router/commit/ee2622828c2ce83807f006a459ac4e11406d9258)) 111 | -------------------------------------------------------------------------------- /example/public/components/ui-router/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2014 The AngularUI Team, Karsten Sperling 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /example/public/components/ui-router/api/angular-ui-router.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for Angular JS 1.1.5+ (ui.router module) 2 | // Project: https://github.com/angular-ui/ui-router 3 | // Definitions by: Michel Salib 4 | // Definitions: https://github.com/borisyankov/DefinitelyTyped 5 | 6 | declare module ng.ui { 7 | 8 | interface IState { 9 | name?: string; 10 | template?: string; 11 | templateUrl?: any; // string || () => string 12 | templateProvider?: any; // () => string || IPromise 13 | controller?: any; 14 | controllerAs?: string; 15 | controllerProvider?: any; 16 | resolve?: {}; 17 | url?: string; 18 | params?: any[]; 19 | views?: {}; 20 | abstract?: boolean; 21 | onEnter?: (...args: any[]) => void; 22 | onExit?: (...args: any[]) => void; 23 | data?: any; 24 | } 25 | 26 | interface ITypedState { 27 | name?: string; 28 | template?: string; 29 | templateUrl?: string; 30 | templateProvider?: () => string; 31 | controller?: any; 32 | controllerAs?: string; 33 | controllerProvider?: any; 34 | resolve?: {}; 35 | url?: string; 36 | params?: any[]; 37 | views?: {}; 38 | abstract?: boolean; 39 | onEnter?: (...args: any[]) => void; 40 | onExit?: (...args: any[]) => void; 41 | data?: T; 42 | } 43 | 44 | interface IStateProvider extends IServiceProvider { 45 | state(name: string, config: IState): IStateProvider; 46 | state(config: IState): IStateProvider; 47 | decorator(name?: string, decorator?: (state: IState, parent: Function) => any): any; 48 | } 49 | 50 | interface IUrlMatcher { 51 | concat(pattern: string): IUrlMatcher; 52 | exec(path: string, searchParams: {}): {}; 53 | parameters(): string[]; 54 | format(values: {}): string; 55 | } 56 | 57 | interface IUrlMatcherFactory { 58 | compile(pattern: string): IUrlMatcher; 59 | isMatcher(o: any): boolean; 60 | } 61 | 62 | interface IUrlRouterProvider extends IServiceProvider { 63 | when(whenPath: RegExp, handler: Function): IUrlRouterProvider; 64 | when(whenPath: RegExp, handler: any[]): IUrlRouterProvider; 65 | when(whenPath: RegExp, toPath: string): IUrlRouterProvider; 66 | when(whenPath: IUrlMatcher, hanlder: Function): IUrlRouterProvider; 67 | when(whenPath: IUrlMatcher, handler: any[]): IUrlRouterProvider; 68 | when(whenPath: IUrlMatcher, toPath: string): IUrlRouterProvider; 69 | when(whenPath: string, handler: Function): IUrlRouterProvider; 70 | when(whenPath: string, handler: any[]): IUrlRouterProvider; 71 | when(whenPath: string, toPath: string): IUrlRouterProvider; 72 | otherwise(handler: Function): IUrlRouterProvider; 73 | otherwise(handler: any[]): IUrlRouterProvider; 74 | otherwise(path: string): IUrlRouterProvider; 75 | rule(handler: Function): IUrlRouterProvider; 76 | rule(handler: any[]): IUrlRouterProvider; 77 | } 78 | 79 | interface IStateOptions { 80 | location?: any; 81 | inherit?: boolean; 82 | relative?: IState; 83 | notify?: boolean; 84 | } 85 | 86 | interface IHrefOptions { 87 | lossy?: boolean; 88 | inherit?: boolean; 89 | relative?: IState; 90 | absolute?: boolean; 91 | } 92 | 93 | interface IStateService { 94 | go(to: string, params?: {}, options?: IStateOptions): IPromise; 95 | transitionTo(state: string, params?: {}, updateLocation?: boolean): void; 96 | transitionTo(state: string, params?: {}, options?: IStateOptions): void; 97 | includes(state: string, params?: {}): boolean; 98 | is(state:string, params?: {}): boolean; 99 | is(state: IState, params?: {}): boolean; 100 | href(state: IState, params?: {}, options?: IHrefOptions): string; 101 | href(state: string, params?: {}, options?: IHrefOptions): string; 102 | get(state: string): IState; 103 | get(): IState[]; 104 | current: IState; 105 | params: any; 106 | reload(): void; 107 | } 108 | 109 | interface IStateParamsService { 110 | [key: string]: any; 111 | } 112 | 113 | interface IStateParams { 114 | [key: string]: any; 115 | } 116 | 117 | interface IUrlRouterService { 118 | /* 119 | * Triggers an update; the same update that happens when the address bar 120 | * url changes, aka $locationChangeSuccess. 121 | * 122 | * This method is useful when you need to use preventDefault() on the 123 | * $locationChangeSuccess event, perform some custom logic (route protection, 124 | * auth, config, redirection, etc) and then finally proceed with the transition 125 | * by calling $urlRouter.sync(). 126 | * 127 | */ 128 | sync(): void; 129 | } 130 | 131 | interface IUiViewScrollProvider { 132 | /* 133 | * Reverts back to using the core $anchorScroll service for scrolling 134 | * based on the url anchor. 135 | */ 136 | useAnchorScroll(): void; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /example/public/components/ui-router/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-ui-router", 3 | "version": "0.2.11", 4 | "main": "./release/angular-ui-router.js", 5 | "dependencies": { 6 | "angular": ">= 1.0.8" 7 | }, 8 | "ignore": [ 9 | "**/.*", 10 | "node_modules", 11 | "bower_components", 12 | "component.json", 13 | "package.json", 14 | "lib", 15 | "config", 16 | "sample", 17 | "test", 18 | "tests", 19 | "ngdoc_assets", 20 | "Gruntfile.js", 21 | "files.js" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /example/public/components/ui-router/src/common.js: -------------------------------------------------------------------------------- 1 | /*jshint globalstrict:true*/ 2 | /*global angular:false*/ 3 | 'use strict'; 4 | 5 | var isDefined = angular.isDefined, 6 | isFunction = angular.isFunction, 7 | isString = angular.isString, 8 | isObject = angular.isObject, 9 | isArray = angular.isArray, 10 | forEach = angular.forEach, 11 | extend = angular.extend, 12 | copy = angular.copy; 13 | 14 | function inherit(parent, extra) { 15 | return extend(new (extend(function() {}, { prototype: parent }))(), extra); 16 | } 17 | 18 | function merge(dst) { 19 | forEach(arguments, function(obj) { 20 | if (obj !== dst) { 21 | forEach(obj, function(value, key) { 22 | if (!dst.hasOwnProperty(key)) dst[key] = value; 23 | }); 24 | } 25 | }); 26 | return dst; 27 | } 28 | 29 | /** 30 | * Finds the common ancestor path between two states. 31 | * 32 | * @param {Object} first The first state. 33 | * @param {Object} second The second state. 34 | * @return {Array} Returns an array of state names in descending order, not including the root. 35 | */ 36 | function ancestors(first, second) { 37 | var path = []; 38 | 39 | for (var n in first.path) { 40 | if (first.path[n] !== second.path[n]) break; 41 | path.push(first.path[n]); 42 | } 43 | return path; 44 | } 45 | 46 | /** 47 | * IE8-safe wrapper for `Object.keys()`. 48 | * 49 | * @param {Object} object A JavaScript object. 50 | * @return {Array} Returns the keys of the object as an array. 51 | */ 52 | function objectKeys(object) { 53 | if (Object.keys) { 54 | return Object.keys(object); 55 | } 56 | var result = []; 57 | 58 | angular.forEach(object, function(val, key) { 59 | result.push(key); 60 | }); 61 | return result; 62 | } 63 | 64 | /** 65 | * IE8-safe wrapper for `Array.prototype.indexOf()`. 66 | * 67 | * @param {Array} array A JavaScript array. 68 | * @param {*} value A value to search the array for. 69 | * @return {Number} Returns the array index value of `value`, or `-1` if not present. 70 | */ 71 | function arraySearch(array, value) { 72 | if (Array.prototype.indexOf) { 73 | return array.indexOf(value, Number(arguments[2]) || 0); 74 | } 75 | var len = array.length >>> 0, from = Number(arguments[2]) || 0; 76 | from = (from < 0) ? Math.ceil(from) : Math.floor(from); 77 | 78 | if (from < 0) from += len; 79 | 80 | for (; from < len; from++) { 81 | if (from in array && array[from] === value) return from; 82 | } 83 | return -1; 84 | } 85 | 86 | /** 87 | * Merges a set of parameters with all parameters inherited between the common parents of the 88 | * current state and a given destination state. 89 | * 90 | * @param {Object} currentParams The value of the current state parameters ($stateParams). 91 | * @param {Object} newParams The set of parameters which will be composited with inherited params. 92 | * @param {Object} $current Internal definition of object representing the current state. 93 | * @param {Object} $to Internal definition of object representing state to transition to. 94 | */ 95 | function inheritParams(currentParams, newParams, $current, $to) { 96 | var parents = ancestors($current, $to), parentParams, inherited = {}, inheritList = []; 97 | 98 | for (var i in parents) { 99 | if (!parents[i].params) continue; 100 | parentParams = objectKeys(parents[i].params); 101 | if (!parentParams.length) continue; 102 | 103 | for (var j in parentParams) { 104 | if (arraySearch(inheritList, parentParams[j]) >= 0) continue; 105 | inheritList.push(parentParams[j]); 106 | inherited[parentParams[j]] = currentParams[parentParams[j]]; 107 | } 108 | } 109 | return extend({}, inherited, newParams); 110 | } 111 | 112 | /** 113 | * Performs a non-strict comparison of the subset of two objects, defined by a list of keys. 114 | * 115 | * @param {Object} a The first object. 116 | * @param {Object} b The second object. 117 | * @param {Array} keys The list of keys within each object to compare. If the list is empty or not specified, 118 | * it defaults to the list of keys in `a`. 119 | * @return {Boolean} Returns `true` if the keys match, otherwise `false`. 120 | */ 121 | function equalForKeys(a, b, keys) { 122 | if (!keys) { 123 | keys = []; 124 | for (var n in a) keys.push(n); // Used instead of Object.keys() for IE8 compatibility 125 | } 126 | 127 | for (var i=0; i 212 | * 213 | * 214 | * 215 | * 216 | * 217 | * 218 | * 222 | * 223 | * 224 | * 225 | * 226 | * 227 | */ 228 | angular.module('ui.router', ['ui.router.state']); 229 | 230 | angular.module('ui.router.compat', ['ui.router']); 231 | -------------------------------------------------------------------------------- /example/public/components/ui-router/src/resolve.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @ngdoc object 3 | * @name ui.router.util.$resolve 4 | * 5 | * @requires $q 6 | * @requires $injector 7 | * 8 | * @description 9 | * Manages resolution of (acyclic) graphs of promises. 10 | */ 11 | $Resolve.$inject = ['$q', '$injector']; 12 | function $Resolve( $q, $injector) { 13 | 14 | var VISIT_IN_PROGRESS = 1, 15 | VISIT_DONE = 2, 16 | NOTHING = {}, 17 | NO_DEPENDENCIES = [], 18 | NO_LOCALS = NOTHING, 19 | NO_PARENT = extend($q.when(NOTHING), { $$promises: NOTHING, $$values: NOTHING }); 20 | 21 | 22 | /** 23 | * @ngdoc function 24 | * @name ui.router.util.$resolve#study 25 | * @methodOf ui.router.util.$resolve 26 | * 27 | * @description 28 | * Studies a set of invocables that are likely to be used multiple times. 29 | *
 30 |    * $resolve.study(invocables)(locals, parent, self)
 31 |    * 
32 | * is equivalent to 33 | *
 34 |    * $resolve.resolve(invocables, locals, parent, self)
 35 |    * 
36 | * but the former is more efficient (in fact `resolve` just calls `study` 37 | * internally). 38 | * 39 | * @param {object} invocables Invocable objects 40 | * @return {function} a function to pass in locals, parent and self 41 | */ 42 | this.study = function (invocables) { 43 | if (!isObject(invocables)) throw new Error("'invocables' must be an object"); 44 | 45 | // Perform a topological sort of invocables to build an ordered plan 46 | var plan = [], cycle = [], visited = {}; 47 | function visit(value, key) { 48 | if (visited[key] === VISIT_DONE) return; 49 | 50 | cycle.push(key); 51 | if (visited[key] === VISIT_IN_PROGRESS) { 52 | cycle.splice(0, cycle.indexOf(key)); 53 | throw new Error("Cyclic dependency: " + cycle.join(" -> ")); 54 | } 55 | visited[key] = VISIT_IN_PROGRESS; 56 | 57 | if (isString(value)) { 58 | plan.push(key, [ function() { return $injector.get(value); }], NO_DEPENDENCIES); 59 | } else { 60 | var params = $injector.annotate(value); 61 | forEach(params, function (param) { 62 | if (param !== key && invocables.hasOwnProperty(param)) visit(invocables[param], param); 63 | }); 64 | plan.push(key, value, params); 65 | } 66 | 67 | cycle.pop(); 68 | visited[key] = VISIT_DONE; 69 | } 70 | forEach(invocables, visit); 71 | invocables = cycle = visited = null; // plan is all that's required 72 | 73 | function isResolve(value) { 74 | return isObject(value) && value.then && value.$$promises; 75 | } 76 | 77 | return function (locals, parent, self) { 78 | if (isResolve(locals) && self === undefined) { 79 | self = parent; parent = locals; locals = null; 80 | } 81 | if (!locals) locals = NO_LOCALS; 82 | else if (!isObject(locals)) { 83 | throw new Error("'locals' must be an object"); 84 | } 85 | if (!parent) parent = NO_PARENT; 86 | else if (!isResolve(parent)) { 87 | throw new Error("'parent' must be a promise returned by $resolve.resolve()"); 88 | } 89 | 90 | // To complete the overall resolution, we have to wait for the parent 91 | // promise and for the promise for each invokable in our plan. 92 | var resolution = $q.defer(), 93 | result = resolution.promise, 94 | promises = result.$$promises = {}, 95 | values = extend({}, locals), 96 | wait = 1 + plan.length/3, 97 | merged = false; 98 | 99 | function done() { 100 | // Merge parent values we haven't got yet and publish our own $$values 101 | if (!--wait) { 102 | if (!merged) merge(values, parent.$$values); 103 | result.$$values = values; 104 | result.$$promises = true; // keep for isResolve() 105 | delete result.$$inheritedValues; 106 | resolution.resolve(values); 107 | } 108 | } 109 | 110 | function fail(reason) { 111 | result.$$failure = reason; 112 | resolution.reject(reason); 113 | } 114 | 115 | // Short-circuit if parent has already failed 116 | if (isDefined(parent.$$failure)) { 117 | fail(parent.$$failure); 118 | return result; 119 | } 120 | 121 | if (parent.$$inheritedValues) { 122 | merge(values, parent.$$inheritedValues); 123 | } 124 | 125 | // Merge parent values if the parent has already resolved, or merge 126 | // parent promises and wait if the parent resolve is still in progress. 127 | if (parent.$$values) { 128 | merged = merge(values, parent.$$values); 129 | result.$$inheritedValues = parent.$$values; 130 | done(); 131 | } else { 132 | if (parent.$$inheritedValues) { 133 | result.$$inheritedValues = parent.$$inheritedValues; 134 | } 135 | extend(promises, parent.$$promises); 136 | parent.then(done, fail); 137 | } 138 | 139 | // Process each invocable in the plan, but ignore any where a local of the same name exists. 140 | for (var i=0, ii=plan.length; i` tag) to a state. If the state has an associated 28 | * URL, the directive will automatically generate & update the `href` attribute via 29 | * the {@link ui.router.state.$state#methods_href $state.href()} method. Clicking 30 | * the link will trigger a state transition with optional parameters. 31 | * 32 | * Also middle-clicking, right-clicking, and ctrl-clicking on the link will be 33 | * handled natively by the browser. 34 | * 35 | * You can also use relative state paths within ui-sref, just like the relative 36 | * paths passed to `$state.go()`. You just need to be aware that the path is relative 37 | * to the state that the link lives in, in other words the state that loaded the 38 | * template containing the link. 39 | * 40 | * You can specify options to pass to {@link ui.router.state.$state#go $state.go()} 41 | * using the `ui-sref-opts` attribute. Options are restricted to `location`, `inherit`, 42 | * and `reload`. 43 | * 44 | * @example 45 | * Here's an example of how you'd use ui-sref and how it would compile. If you have the 46 | * following template: 47 | *
 48 |  * Home | About | Next page
 49 |  * 
 50 |  * 
 55 |  * 
56 | * 57 | * Then the compiled html would be (assuming Html5Mode is off and current state is contacts): 58 | *
 59 |  * Home | About | Next page
 60 |  * 
 61 |  * 
    62 | *
  • 63 | * Joe 64 | *
  • 65 | *
  • 66 | * Alice 67 | *
  • 68 | *
  • 69 | * Bob 70 | *
  • 71 | *
72 | * 73 | * Home 74 | *
75 | * 76 | * @param {string} ui-sref 'stateName' can be any valid absolute or relative state 77 | * @param {Object} ui-sref-opts options to pass to {@link ui.router.state.$state#go $state.go()} 78 | */ 79 | $StateRefDirective.$inject = ['$state', '$timeout']; 80 | function $StateRefDirective($state, $timeout) { 81 | var allowedOptions = ['location', 'inherit', 'reload']; 82 | 83 | return { 84 | restrict: 'A', 85 | require: ['?^uiSrefActive', '?^uiSrefActiveEq'], 86 | link: function(scope, element, attrs, uiSrefActive) { 87 | var ref = parseStateRef(attrs.uiSref, $state.current.name); 88 | var params = null, url = null, base = stateContext(element) || $state.$current; 89 | var isForm = element[0].nodeName === "FORM"; 90 | var attr = isForm ? "action" : "href", nav = true; 91 | 92 | var options = { relative: base, inherit: true }; 93 | var optionsOverride = scope.$eval(attrs.uiSrefOpts) || {}; 94 | 95 | angular.forEach(allowedOptions, function(option) { 96 | if (option in optionsOverride) { 97 | options[option] = optionsOverride[option]; 98 | } 99 | }); 100 | 101 | var update = function(newVal) { 102 | if (newVal) params = newVal; 103 | if (!nav) return; 104 | 105 | var newHref = $state.href(ref.state, params, options); 106 | 107 | var activeDirective = uiSrefActive[1] || uiSrefActive[0]; 108 | if (activeDirective) { 109 | activeDirective.$$setStateInfo(ref.state, params); 110 | } 111 | if (newHref === null) { 112 | nav = false; 113 | return false; 114 | } 115 | element[0][attr] = newHref; 116 | }; 117 | 118 | if (ref.paramExpr) { 119 | scope.$watch(ref.paramExpr, function(newVal, oldVal) { 120 | if (newVal !== params) update(newVal); 121 | }, true); 122 | params = scope.$eval(ref.paramExpr); 123 | } 124 | update(); 125 | 126 | if (isForm) return; 127 | 128 | element.bind("click", function(e) { 129 | var button = e.which || e.button; 130 | if ( !(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || element.attr('target')) ) { 131 | // HACK: This is to allow ng-clicks to be processed before the transition is initiated: 132 | var transition = $timeout(function() { 133 | $state.go(ref.state, params, options); 134 | }); 135 | e.preventDefault(); 136 | 137 | e.preventDefault = function() { 138 | $timeout.cancel(transition); 139 | }; 140 | } 141 | }); 142 | } 143 | }; 144 | } 145 | 146 | /** 147 | * @ngdoc directive 148 | * @name ui.router.state.directive:ui-sref-active 149 | * 150 | * @requires ui.router.state.$state 151 | * @requires ui.router.state.$stateParams 152 | * @requires $interpolate 153 | * 154 | * @restrict A 155 | * 156 | * @description 157 | * A directive working alongside ui-sref to add classes to an element when the 158 | * related ui-sref directive's state is active, and removing them when it is inactive. 159 | * The primary use-case is to simplify the special appearance of navigation menus 160 | * relying on `ui-sref`, by having the "active" state's menu button appear different, 161 | * distinguishing it from the inactive menu items. 162 | * 163 | * ui-sref-active can live on the same element as ui-sref or on a parent element. The first 164 | * ui-sref-active found at the same level or above the ui-sref will be used. 165 | * 166 | * Will activate when the ui-sref's target state or any child state is active. If you 167 | * need to activate only when the ui-sref target state is active and *not* any of 168 | * it's children, then you will use 169 | * {@link ui.router.state.directive:ui-sref-active-eq ui-sref-active-eq} 170 | * 171 | * @example 172 | * Given the following template: 173 | *
174 |  * 
179 |  * 
180 | * 181 | * 182 | * When the app state is "app.user" (or any children states), and contains the state parameter "user" with value "bilbobaggins", 183 | * the resulting HTML will appear as (note the 'active' class): 184 | *
185 |  * 
190 |  * 
191 | * 192 | * The class name is interpolated **once** during the directives link time (any further changes to the 193 | * interpolated value are ignored). 194 | * 195 | * Multiple classes may be specified in a space-separated format: 196 | *
197 |  * 
    198 | *
  • 199 | * link 200 | *
  • 201 | *
202 | *
203 | */ 204 | 205 | /** 206 | * @ngdoc directive 207 | * @name ui.router.state.directive:ui-sref-active-eq 208 | * 209 | * @requires ui.router.state.$state 210 | * @requires ui.router.state.$stateParams 211 | * @requires $interpolate 212 | * 213 | * @restrict A 214 | * 215 | * @description 216 | * The same as {@link ui.router.state.directive:ui-sref-active ui-sref-active} but will will only activate 217 | * when the exact target state used in the `ui-sref` is active; no child states. 218 | * 219 | */ 220 | $StateRefActiveDirective.$inject = ['$state', '$stateParams', '$interpolate']; 221 | function $StateRefActiveDirective($state, $stateParams, $interpolate) { 222 | return { 223 | restrict: "A", 224 | controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) { 225 | var state, params, activeClass; 226 | 227 | // There probably isn't much point in $observing this 228 | // uiSrefActive and uiSrefActiveEq share the same directive object with some 229 | // slight difference in logic routing 230 | activeClass = $interpolate($attrs.uiSrefActiveEq || $attrs.uiSrefActive || '', false)($scope); 231 | 232 | // Allow uiSref to communicate with uiSrefActive[Equals] 233 | this.$$setStateInfo = function (newState, newParams) { 234 | state = $state.get(newState, stateContext($element)); 235 | params = newParams; 236 | update(); 237 | }; 238 | 239 | $scope.$on('$stateChangeSuccess', update); 240 | 241 | // Update route state 242 | function update() { 243 | if (isMatch()) { 244 | $element.addClass(activeClass); 245 | } else { 246 | $element.removeClass(activeClass); 247 | } 248 | } 249 | 250 | function isMatch() { 251 | if (typeof $attrs.uiSrefActiveEq !== 'undefined') { 252 | return $state.$current.self === state && matchesParams(); 253 | } else { 254 | return $state.includes(state.name) && matchesParams(); 255 | } 256 | } 257 | 258 | function matchesParams() { 259 | return !params || equalForKeys(params, $stateParams); 260 | } 261 | }] 262 | }; 263 | } 264 | 265 | angular.module('ui.router.state') 266 | .directive('uiSref', $StateRefDirective) 267 | .directive('uiSrefActive', $StateRefActiveDirective) 268 | .directive('uiSrefActiveEq', $StateRefActiveDirective); 269 | -------------------------------------------------------------------------------- /example/public/components/ui-router/src/stateFilters.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @ngdoc filter 3 | * @name ui.router.state.filter:isState 4 | * 5 | * @requires ui.router.state.$state 6 | * 7 | * @description 8 | * Translates to {@link ui.router.state.$state#methods_is $state.is("stateName")}. 9 | */ 10 | $IsStateFilter.$inject = ['$state']; 11 | function $IsStateFilter($state) { 12 | return function(state) { 13 | return $state.is(state); 14 | }; 15 | } 16 | 17 | /** 18 | * @ngdoc filter 19 | * @name ui.router.state.filter:includedByState 20 | * 21 | * @requires ui.router.state.$state 22 | * 23 | * @description 24 | * Translates to {@link ui.router.state.$state#methods_includes $state.includes('fullOrPartialStateName')}. 25 | */ 26 | $IncludedByStateFilter.$inject = ['$state']; 27 | function $IncludedByStateFilter($state) { 28 | return function(state) { 29 | return $state.includes(state); 30 | }; 31 | } 32 | 33 | angular.module('ui.router.state') 34 | .filter('isState', $IsStateFilter) 35 | .filter('includedByState', $IncludedByStateFilter); 36 | -------------------------------------------------------------------------------- /example/public/components/ui-router/src/templateFactory.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @ngdoc object 3 | * @name ui.router.util.$templateFactory 4 | * 5 | * @requires $http 6 | * @requires $templateCache 7 | * @requires $injector 8 | * 9 | * @description 10 | * Service. Manages loading of templates. 11 | */ 12 | $TemplateFactory.$inject = ['$http', '$templateCache', '$injector']; 13 | function $TemplateFactory( $http, $templateCache, $injector) { 14 | 15 | /** 16 | * @ngdoc function 17 | * @name ui.router.util.$templateFactory#fromConfig 18 | * @methodOf ui.router.util.$templateFactory 19 | * 20 | * @description 21 | * Creates a template from a configuration object. 22 | * 23 | * @param {object} config Configuration object for which to load a template. 24 | * The following properties are search in the specified order, and the first one 25 | * that is defined is used to create the template: 26 | * 27 | * @param {string|object} config.template html string template or function to 28 | * load via {@link ui.router.util.$templateFactory#fromString fromString}. 29 | * @param {string|object} config.templateUrl url to load or a function returning 30 | * the url to load via {@link ui.router.util.$templateFactory#fromUrl fromUrl}. 31 | * @param {Function} config.templateProvider function to invoke via 32 | * {@link ui.router.util.$templateFactory#fromProvider fromProvider}. 33 | * @param {object} params Parameters to pass to the template function. 34 | * @param {object} locals Locals to pass to `invoke` if the template is loaded 35 | * via a `templateProvider`. Defaults to `{ params: params }`. 36 | * 37 | * @return {string|object} The template html as a string, or a promise for 38 | * that string,or `null` if no template is configured. 39 | */ 40 | this.fromConfig = function (config, params, locals) { 41 | return ( 42 | isDefined(config.template) ? this.fromString(config.template, params) : 43 | isDefined(config.templateUrl) ? this.fromUrl(config.templateUrl, params) : 44 | isDefined(config.templateProvider) ? this.fromProvider(config.templateProvider, params, locals) : 45 | null 46 | ); 47 | }; 48 | 49 | /** 50 | * @ngdoc function 51 | * @name ui.router.util.$templateFactory#fromString 52 | * @methodOf ui.router.util.$templateFactory 53 | * 54 | * @description 55 | * Creates a template from a string or a function returning a string. 56 | * 57 | * @param {string|object} template html template as a string or function that 58 | * returns an html template as a string. 59 | * @param {object} params Parameters to pass to the template function. 60 | * 61 | * @return {string|object} The template html as a string, or a promise for that 62 | * string. 63 | */ 64 | this.fromString = function (template, params) { 65 | return isFunction(template) ? template(params) : template; 66 | }; 67 | 68 | /** 69 | * @ngdoc function 70 | * @name ui.router.util.$templateFactory#fromUrl 71 | * @methodOf ui.router.util.$templateFactory 72 | * 73 | * @description 74 | * Loads a template from the a URL via `$http` and `$templateCache`. 75 | * 76 | * @param {string|Function} url url of the template to load, or a function 77 | * that returns a url. 78 | * @param {Object} params Parameters to pass to the url function. 79 | * @return {string|Promise.} The template html as a string, or a promise 80 | * for that string. 81 | */ 82 | this.fromUrl = function (url, params) { 83 | if (isFunction(url)) url = url(params); 84 | if (url == null) return null; 85 | else return $http 86 | .get(url, { cache: $templateCache }) 87 | .then(function(response) { return response.data; }); 88 | }; 89 | 90 | /** 91 | * @ngdoc function 92 | * @name ui.router.util.$templateFactory#fromProvider 93 | * @methodOf ui.router.util.$templateFactory 94 | * 95 | * @description 96 | * Creates a template by invoking an injectable provider function. 97 | * 98 | * @param {Function} provider Function to invoke via `$injector.invoke` 99 | * @param {Object} params Parameters for the template. 100 | * @param {Object} locals Locals to pass to `invoke`. Defaults to 101 | * `{ params: params }`. 102 | * @return {string|Promise.} The template html as a string, or a promise 103 | * for that string. 104 | */ 105 | this.fromProvider = function (provider, params, locals) { 106 | return $injector.invoke(provider, null, locals || { params: params }); 107 | }; 108 | } 109 | 110 | angular.module('ui.router.util').service('$templateFactory', $TemplateFactory); 111 | -------------------------------------------------------------------------------- /example/public/components/ui-router/src/view.js: -------------------------------------------------------------------------------- 1 | 2 | $ViewProvider.$inject = []; 3 | function $ViewProvider() { 4 | 5 | this.$get = $get; 6 | /** 7 | * @ngdoc object 8 | * @name ui.router.state.$view 9 | * 10 | * @requires ui.router.util.$templateFactory 11 | * @requires $rootScope 12 | * 13 | * @description 14 | * 15 | */ 16 | $get.$inject = ['$rootScope', '$templateFactory']; 17 | function $get( $rootScope, $templateFactory) { 18 | return { 19 | // $view.load('full.viewName', { template: ..., controller: ..., resolve: ..., async: false, params: ... }) 20 | /** 21 | * @ngdoc function 22 | * @name ui.router.state.$view#load 23 | * @methodOf ui.router.state.$view 24 | * 25 | * @description 26 | * 27 | * @param {string} name name 28 | * @param {object} options option object. 29 | */ 30 | load: function load(name, options) { 31 | var result, defaults = { 32 | template: null, controller: null, view: null, locals: null, notify: true, async: true, params: {} 33 | }; 34 | options = extend(defaults, options); 35 | 36 | if (options.view) { 37 | result = $templateFactory.fromConfig(options.view, options.params, options.locals); 38 | } 39 | if (result && options.notify) { 40 | /** 41 | * @ngdoc event 42 | * @name ui.router.state.$state#$viewContentLoading 43 | * @eventOf ui.router.state.$view 44 | * @eventType broadcast on root scope 45 | * @description 46 | * 47 | * Fired once the view **begins loading**, *before* the DOM is rendered. 48 | * 49 | * @param {Object} event Event object. 50 | * @param {Object} viewConfig The view config properties (template, controller, etc). 51 | * 52 | * @example 53 | * 54 | *
55 |          * $scope.$on('$viewContentLoading',
56 |          * function(event, viewConfig){
57 |          *     // Access to all the view config properties.
58 |          *     // and one special property 'targetView'
59 |          *     // viewConfig.targetView
60 |          * });
61 |          * 
62 | */ 63 | $rootScope.$broadcast('$viewContentLoading', options); 64 | } 65 | return result; 66 | } 67 | }; 68 | } 69 | } 70 | 71 | angular.module('ui.router.state').provider('$view', $ViewProvider); 72 | -------------------------------------------------------------------------------- /example/public/components/ui-router/src/viewDirective.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @ngdoc directive 3 | * @name ui.router.state.directive:ui-view 4 | * 5 | * @requires ui.router.state.$state 6 | * @requires $compile 7 | * @requires $controller 8 | * @requires $injector 9 | * @requires ui.router.state.$uiViewScroll 10 | * @requires $document 11 | * 12 | * @restrict ECA 13 | * 14 | * @description 15 | * The ui-view directive tells $state where to place your templates. 16 | * 17 | * @param {string=} ui-view A view name. The name should be unique amongst the other views in the 18 | * same state. You can have views of the same name that live in different states. 19 | * 20 | * @param {string=} autoscroll It allows you to set the scroll behavior of the browser window 21 | * when a view is populated. By default, $anchorScroll is overridden by ui-router's custom scroll 22 | * service, {@link ui.router.state.$uiViewScroll}. This custom service let's you 23 | * scroll ui-view elements into view when they are populated during a state activation. 24 | * 25 | * *Note: To revert back to old [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll) 26 | * functionality, call `$uiViewScrollProvider.useAnchorScroll()`.* 27 | * 28 | * @param {string=} onload Expression to evaluate whenever the view updates. 29 | * 30 | * @example 31 | * A view can be unnamed or named. 32 | *
 33 |  * 
 34 |  * 
35 | * 36 | * 37 | *
38 | *
39 | * 40 | * You can only have one unnamed view within any template (or root html). If you are only using a 41 | * single view and it is unnamed then you can populate it like so: 42 | *
 43 |  * 
44 | * $stateProvider.state("home", { 45 | * template: "

HELLO!

" 46 | * }) 47 | *
48 | * 49 | * The above is a convenient shortcut equivalent to specifying your view explicitly with the {@link ui.router.state.$stateProvider#views `views`} 50 | * config property, by name, in this case an empty name: 51 | *
 52 |  * $stateProvider.state("home", {
 53 |  *   views: {
 54 |  *     "": {
 55 |  *       template: "

HELLO!

" 56 | * } 57 | * } 58 | * }) 59 | *
60 | * 61 | * But typically you'll only use the views property if you name your view or have more than one view 62 | * in the same template. There's not really a compelling reason to name a view if its the only one, 63 | * but you could if you wanted, like so: 64 | *
 65 |  * 
66 | *
67 | *
 68 |  * $stateProvider.state("home", {
 69 |  *   views: {
 70 |  *     "main": {
 71 |  *       template: "

HELLO!

" 72 | * } 73 | * } 74 | * }) 75 | *
76 | * 77 | * Really though, you'll use views to set up multiple views: 78 | *
 79 |  * 
80 | *
81 | *
82 | *
83 | * 84 | *
 85 |  * $stateProvider.state("home", {
 86 |  *   views: {
 87 |  *     "": {
 88 |  *       template: "

HELLO!

" 89 | * }, 90 | * "chart": { 91 | * template: "" 92 | * }, 93 | * "data": { 94 | * template: "" 95 | * } 96 | * } 97 | * }) 98 | *
99 | * 100 | * Examples for `autoscroll`: 101 | * 102 | *
103 |  * 
105 |  * 
106 |  *
107 |  * 
109 |  * 
110 |  * 
111 |  * 
112 |  * 
113 | */ 114 | $ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll']; 115 | function $ViewDirective( $state, $injector, $uiViewScroll) { 116 | 117 | function getService() { 118 | return ($injector.has) ? function(service) { 119 | return $injector.has(service) ? $injector.get(service) : null; 120 | } : function(service) { 121 | try { 122 | return $injector.get(service); 123 | } catch (e) { 124 | return null; 125 | } 126 | }; 127 | } 128 | 129 | var service = getService(), 130 | $animator = service('$animator'), 131 | $animate = service('$animate'); 132 | 133 | // Returns a set of DOM manipulation functions based on which Angular version 134 | // it should use 135 | function getRenderer(attrs, scope) { 136 | var statics = function() { 137 | return { 138 | enter: function (element, target, cb) { target.after(element); cb(); }, 139 | leave: function (element, cb) { element.remove(); cb(); } 140 | }; 141 | }; 142 | 143 | if ($animate) { 144 | return { 145 | enter: function(element, target, cb) { $animate.enter(element, null, target, cb); }, 146 | leave: function(element, cb) { $animate.leave(element, cb); } 147 | }; 148 | } 149 | 150 | if ($animator) { 151 | var animate = $animator && $animator(scope, attrs); 152 | 153 | return { 154 | enter: function(element, target, cb) {animate.enter(element, null, target); cb(); }, 155 | leave: function(element, cb) { animate.leave(element); cb(); } 156 | }; 157 | } 158 | 159 | return statics(); 160 | } 161 | 162 | var directive = { 163 | restrict: 'ECA', 164 | terminal: true, 165 | priority: 400, 166 | transclude: 'element', 167 | compile: function (tElement, tAttrs, $transclude) { 168 | return function (scope, $element, attrs) { 169 | var previousEl, currentEl, currentScope, latestLocals, 170 | onloadExp = attrs.onload || '', 171 | autoScrollExp = attrs.autoscroll, 172 | renderer = getRenderer(attrs, scope); 173 | 174 | scope.$on('$stateChangeSuccess', function() { 175 | updateView(false); 176 | }); 177 | scope.$on('$viewContentLoading', function() { 178 | updateView(false); 179 | }); 180 | 181 | updateView(true); 182 | 183 | function cleanupLastView() { 184 | if (previousEl) { 185 | previousEl.remove(); 186 | previousEl = null; 187 | } 188 | 189 | if (currentScope) { 190 | currentScope.$destroy(); 191 | currentScope = null; 192 | } 193 | 194 | if (currentEl) { 195 | renderer.leave(currentEl, function() { 196 | previousEl = null; 197 | }); 198 | 199 | previousEl = currentEl; 200 | currentEl = null; 201 | } 202 | } 203 | 204 | function updateView(firstTime) { 205 | var newScope, 206 | name = getUiViewName(attrs, $element.inheritedData('$uiView')), 207 | previousLocals = name && $state.$current && $state.$current.locals[name]; 208 | 209 | if (!firstTime && previousLocals === latestLocals) return; // nothing to do 210 | newScope = scope.$new(); 211 | latestLocals = $state.$current.locals[name]; 212 | 213 | var clone = $transclude(newScope, function(clone) { 214 | renderer.enter(clone, $element, function onUiViewEnter() { 215 | if (angular.isDefined(autoScrollExp) && !autoScrollExp || scope.$eval(autoScrollExp)) { 216 | $uiViewScroll(clone); 217 | } 218 | }); 219 | cleanupLastView(); 220 | }); 221 | 222 | currentEl = clone; 223 | currentScope = newScope; 224 | /** 225 | * @ngdoc event 226 | * @name ui.router.state.directive:ui-view#$viewContentLoaded 227 | * @eventOf ui.router.state.directive:ui-view 228 | * @eventType emits on ui-view directive scope 229 | * @description * 230 | * Fired once the view is **loaded**, *after* the DOM is rendered. 231 | * 232 | * @param {Object} event Event object. 233 | */ 234 | currentScope.$emit('$viewContentLoaded'); 235 | currentScope.$eval(onloadExp); 236 | } 237 | }; 238 | } 239 | }; 240 | 241 | return directive; 242 | } 243 | 244 | $ViewDirectiveFill.$inject = ['$compile', '$controller', '$state']; 245 | function $ViewDirectiveFill ($compile, $controller, $state) { 246 | return { 247 | restrict: 'ECA', 248 | priority: -400, 249 | compile: function (tElement) { 250 | var initial = tElement.html(); 251 | return function (scope, $element, attrs) { 252 | var current = $state.$current, 253 | name = getUiViewName(attrs, $element.inheritedData('$uiView')), 254 | locals = current && current.locals[name]; 255 | 256 | if (! locals) { 257 | return; 258 | } 259 | 260 | $element.data('$uiView', { name: name, state: locals.$$state }); 261 | $element.html(locals.$template ? locals.$template : initial); 262 | 263 | var link = $compile($element.contents()); 264 | 265 | if (locals.$$controller) { 266 | locals.$scope = scope; 267 | var controller = $controller(locals.$$controller, locals); 268 | if (locals.$$controllerAs) { 269 | scope[locals.$$controllerAs] = controller; 270 | } 271 | $element.data('$ngControllerController', controller); 272 | $element.children().data('$ngControllerController', controller); 273 | } 274 | 275 | link(scope); 276 | }; 277 | } 278 | }; 279 | } 280 | 281 | /** 282 | * Shared ui-view code for both directives: 283 | * Given attributes and inherited $uiView data, return the view's name 284 | */ 285 | function getUiViewName(attrs, inherited) { 286 | var name = attrs.uiView || attrs.name || ''; 287 | return name.indexOf('@') >= 0 ? name : (name + '@' + (inherited ? inherited.state.name : '')); 288 | } 289 | 290 | angular.module('ui.router.state').directive('uiView', $ViewDirective); 291 | angular.module('ui.router.state').directive('uiView', $ViewDirectiveFill); 292 | -------------------------------------------------------------------------------- /example/public/components/ui-router/src/viewScroll.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @ngdoc object 3 | * @name ui.router.state.$uiViewScrollProvider 4 | * 5 | * @description 6 | * Provider that returns the {@link ui.router.state.$uiViewScroll} service function. 7 | */ 8 | function $ViewScrollProvider() { 9 | 10 | var useAnchorScroll = false; 11 | 12 | /** 13 | * @ngdoc function 14 | * @name ui.router.state.$uiViewScrollProvider#useAnchorScroll 15 | * @methodOf ui.router.state.$uiViewScrollProvider 16 | * 17 | * @description 18 | * Reverts back to using the core [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll) service for 19 | * scrolling based on the url anchor. 20 | */ 21 | this.useAnchorScroll = function () { 22 | useAnchorScroll = true; 23 | }; 24 | 25 | /** 26 | * @ngdoc object 27 | * @name ui.router.state.$uiViewScroll 28 | * 29 | * @requires $anchorScroll 30 | * @requires $timeout 31 | * 32 | * @description 33 | * When called with a jqLite element, it scrolls the element into view (after a 34 | * `$timeout` so the DOM has time to refresh). 35 | * 36 | * If you prefer to rely on `$anchorScroll` to scroll the view to the anchor, 37 | * this can be enabled by calling {@link ui.router.state.$uiViewScrollProvider#methods_useAnchorScroll `$uiViewScrollProvider.useAnchorScroll()`}. 38 | */ 39 | this.$get = ['$anchorScroll', '$timeout', function ($anchorScroll, $timeout) { 40 | if (useAnchorScroll) { 41 | return $anchorScroll; 42 | } 43 | 44 | return function ($element) { 45 | $timeout(function () { 46 | $element[0].scrollIntoView(); 47 | }, 0, false); 48 | }; 49 | }]; 50 | } 51 | 52 | angular.module('ui.router.state').provider('$uiViewScroll', $ViewScrollProvider); 53 | -------------------------------------------------------------------------------- /example/public/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Helvetica, Verdana; 3 | margin: 0; 4 | } 5 | 6 | textarea { 7 | padding: 100px; 8 | border: none; 9 | width: 100%; 10 | position: absolute; 11 | bottom: 0; 12 | top: 0; 13 | font-size: 24px; 14 | -webkit-box-sizing: border-box; 15 | -moz-box-sizing: border-box; 16 | box-sizing: border-box; 17 | } 18 | 19 | *:focus { 20 | outline: none; 21 | } 22 | 23 | #setup-area { 24 | text-align: center; 25 | } 26 | 27 | #username { 28 | width: 200px; 29 | height: 30px; 30 | font-size: 24px; 31 | border: none; 32 | border-bottom: 3px solid #df4e69; 33 | font-weight: light; 34 | margin-top: 100px; 35 | } 36 | 37 | .channel-number { 38 | border-radius: 50%; 39 | width: 50px; 40 | height: 50px; 41 | text-align: center; 42 | line-height: 50px; 43 | font-size: 28px; 44 | background-color: #df4e69; 45 | color: white; 46 | border: 3px solid white; 47 | } 48 | 49 | .channel-number:hover { 50 | border: 3px solid #df4e69; 51 | color: #df4e69; 52 | background: white; 53 | cursor: pointer; 54 | } 55 | 56 | .choose-text { 57 | font-size: 24px; 58 | margin-top: 80px; 59 | } 60 | 61 | .channel-chooser { 62 | margin: auto; 63 | margin-top: 30px; 64 | width: 400px; 65 | display: flex; 66 | justify-content: space-between; 67 | } -------------------------------------------------------------------------------- /example/public/js/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('pusherCollaboration', [ 4 | 'pusherCollabControllers', 5 | 'pusherCollabDirectives', 6 | 'pusherCollabServices', 7 | 'ui.router', 8 | 'pusher-angular' 9 | ]) 10 | .config(['$urlRouterProvider', '$locationProvider', '$stateProvider', 11 | function($urlRouterProvider, $locationProvider, $stateProvider) { 12 | $urlRouterProvider.otherwise('/'); 13 | 14 | $stateProvider 15 | .state('home', { 16 | url: '/', 17 | templateUrl: '/views/setup.html', 18 | controller: 'SetupCtrl' 19 | }) 20 | .state('collaborate', { 21 | url: '/collaborate', 22 | templateUrl: '/views/text.html', 23 | controller: 'CollaborationCtrl' 24 | }) 25 | } 26 | ]); 27 | -------------------------------------------------------------------------------- /example/public/js/controllers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var pCControllers = angular.module('pusherCollabControllers', ['pusherCollabServices']); 4 | 5 | pCControllers.controller('CollaborationCtrl', [ 6 | '$scope', 7 | '$pusher', 8 | '$http', 9 | '$state', 10 | 'channelManager', 11 | function ($scope, $pusher, $http, $state, channelManager) { 12 | var channel; 13 | var textzone = document.getElementById('textzone'); 14 | 15 | $scope.members = channelManager.getMembers(); 16 | 17 | $scope.sendUpdate = function (event) { 18 | channel = channelManager.get(); 19 | var num = channelManager.getChannelNum(); 20 | if (channel) { 21 | $http.post('/text-update', { channelNum: num, text: event.srcElement.value, socket_id: channel.client.connection.baseConnection.socket_id }); 22 | } 23 | } 24 | 25 | $scope.$watch('text', function () { 26 | var r = 0; 27 | var temp = $scope.text.replace(/\s/g,' '); 28 | temp = temp.split(' '); 29 | for (var i = 0; i < temp.length; i++) { 30 | if (temp[i].length > 0) 31 | r++; 32 | }; 33 | 34 | $scope.words = r; 35 | $scope.characters = $scope.text.length; 36 | }); 37 | 38 | $scope.paragraphs = 0; 39 | $scope.words = 0; 40 | $scope.characters = 0; 41 | $scope.allCharacters = 0; 42 | } 43 | ]); 44 | 45 | pCControllers.controller('SetupCtrl', [ 46 | '$scope', 47 | '$pusher', 48 | '$http', 49 | '$state', 50 | 'channelManager', 51 | function ($scope, $pusher, $http, $state, channelManager) { 52 | $scope.channel1ReadOnly = false; 53 | $scope.channel2ReadOnly = false; 54 | $scope.channel3ReadOnly = false; 55 | 56 | 57 | $scope.$watch('channel1ReadOnly', function() { 58 | if ($scope.channel1ReadOnly) { 59 | channelManager.set(1, $scope.username); 60 | $state.go('collaborate'); 61 | } 62 | }) 63 | 64 | $scope.$watch('channel2ReadOnly', function() { 65 | if ($scope.channel2ReadOnly) { 66 | channelManager.set(2, $scope.username); 67 | $state.go('collaborate'); 68 | } 69 | }) 70 | 71 | $scope.$watch('channel3ReadOnly', function() { 72 | if ($scope.channel3ReadOnly) { 73 | channelManager.set(3, $scope.username); 74 | $state.go('collaborate'); 75 | } 76 | }) 77 | } 78 | ]); 79 | -------------------------------------------------------------------------------- /example/public/js/directives.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var pCDirectives = angular.module('pusherCollabDirectives', ['pusherCollabServices']); 4 | 5 | pCDirectives.directive('collaborate', ['$pusher', 'channelManager', function($pusher, channelManager) { 6 | var linker = function(scope, elem, attrs) { 7 | scope.text = ''; 8 | 9 | var channel = channelManager.get(); 10 | 11 | function sortNumber(a, b) { 12 | return a - b; 13 | } 14 | 15 | channel.bind('pusher:subscription_succeeded', function (members) { 16 | if (channel.members.count > 1) { 17 | channel.trigger('client-joined', channel.members.me.id); 18 | } 19 | }); 20 | 21 | channel.bind('client-joined', function (id) { 22 | var ids = []; 23 | for (var member in channel.members.members) { 24 | ids.push(member); 25 | } 26 | if (ids.indexOf(id) !== -1) { ids.splice(ids.indexOf(id), 1); } 27 | if (channel.members.me.id !== ids.sort(sortNumber)[0]) { 28 | channel.trigger('client-catchup', { id: id, text: scope.text }); 29 | } 30 | }); 31 | 32 | channel.bind('client-catchup', function (data) { 33 | if (data.id == channel.members.me.id) { 34 | scope.text = data.text; 35 | } 36 | }); 37 | 38 | if (channel) { 39 | channel.bind('update', function (text) { 40 | if (elem.value !== text) { 41 | scope.text = text; 42 | } 43 | }); 44 | } 45 | } 46 | return { 47 | restrict: 'A', 48 | link: linker 49 | } 50 | }]); 51 | -------------------------------------------------------------------------------- /example/public/js/services.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var pCServices = angular.module('pusherCollabServices', []); 4 | 5 | pCDirectives.factory('channelManager', ['$pusher', function($pusher) { 6 | var channel, cNum; 7 | 8 | return { 9 | get: function () { 10 | return channel; 11 | }, 12 | set: function (channelNum, username) { 13 | cNum = channelNum; 14 | var client = new Pusher('YOUR_APP_KEY', { auth: { params: { username: username}} }); 15 | var pusher = $pusher(client); 16 | channel = pusher.subscribe('presence-collaborate-' + channelNum); 17 | return channel; 18 | }, 19 | getChannelNum: function () { 20 | return cNum; 21 | }, 22 | getMembers: function () { 23 | return channel.members; 24 | } 25 | } 26 | }]); 27 | -------------------------------------------------------------------------------- /example/public/views/setup.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 |
Choose a channel
6 |
7 |
1
8 |
2
9 |
3
10 |
11 | 12 |
13 | -------------------------------------------------------------------------------- /example/public/views/text.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 | Me: {{members.me.info.username}}    6 | Members ({{members.count}}): 7 | {{member.username}}{{$last ? '' : ', '}} 8 | 9 |
10 |
11 | 12 | Words: {{words}} 13 | Characters: {{characters}} 14 |
15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /example/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var express = require('express'); 4 | var app = express(); 5 | var bodyParser = require('body-parser'); 6 | var Pusher = require('pusher'); 7 | 8 | app.set('views', __dirname + '/app/views'); 9 | app.set('view engine', 'ejs'); 10 | 11 | var pusherConfig = {}; 12 | try { 13 | pusherConfig = require('./pusherConfig'); 14 | } catch (err) { 15 | pusherConfig.appId = 'YOUR_APP_ID'; 16 | pusherConfig.key = 'YOUR_APP_KEY'; 17 | pusherConfig.secret = 'YOUR_APP_SECRET'; 18 | } 19 | 20 | var pusher = new Pusher(pusherConfig); 21 | var port = Number(process.env.PORT || 3000); 22 | var host = 'localhost'; 23 | 24 | app.use(bodyParser()); 25 | app.use(express.static(__dirname + '/public')); 26 | 27 | app.post('/text-update', function(req, res) { 28 | pusher.trigger('presence-collaborate-' + req.body.channelNum, 'update', req.body.text, req.body.socket_id); 29 | res.send(); 30 | }); 31 | 32 | app.post('/pusher/auth', function(req, res) { 33 | var socketId = req.body.socket_id; 34 | var channel = req.body.channel_name; 35 | var auth = pusher.authenticate( socketId, channel, { 36 | user_id: "" + Math.floor(Math.random() * (1000 - 1) + 1), 37 | user_info: { username: req.body.username } 38 | }); 39 | res.send( auth ); 40 | }); 41 | 42 | app.get('*', function(req, res){ 43 | res.render('index'); 44 | }); 45 | 46 | app.listen(port); 47 | 48 | console.log('Server running at http://'+host+':'+port.toString()+'/'); -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Wed Oct 01 2014 14:31:47 GMT+0100 (BST) 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | // base path that will be used to resolve all patterns (eg. files, exclude) 7 | basePath: '', 8 | 9 | // frameworks to use 10 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 11 | frameworks: ['jasmine'], 12 | 13 | // list of files / patterns to load in the browser 14 | files: [ 15 | 'public/components/angular/angular.js', 16 | 'public/components/angular-mocks/angular-mocks.js', 17 | 'tests/specs.js', 18 | 'lib/pusher-angular.js' 19 | ], 20 | 21 | // list of files to exclude 22 | exclude: [], 23 | 24 | // preprocess matching files before serving them to the browser 25 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 26 | preprocessors: { 27 | 'lib/pusher-angular.js': 'coverage' 28 | }, 29 | 30 | // test results reporter to use 31 | // possible values: 'dots', 'progress' 32 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 33 | reporters: ['progress', 'spec', 'coverage'], 34 | 35 | coverageReporter: { 36 | reporters: [ 37 | { 38 | type : 'text-summary' 39 | }, 40 | { 41 | type: 'lcovonly', 42 | dir: 'coverage', 43 | subdir: '.' 44 | } 45 | ] 46 | }, 47 | 48 | // web server port 49 | port: 9876, 50 | 51 | // enable / disable colors in the output (reporters and logs) 52 | colors: true, 53 | 54 | // level of logging 55 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 56 | logLevel: config.LOG_INFO, 57 | 58 | // enable / disable watching file and executing tests whenever any file changes 59 | autoWatch: false, 60 | 61 | // start these browsers 62 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 63 | // browsers: ['Chrome', 'Firefox', 'Safari', 'Opera'], 64 | browsers: ['Firefox'], 65 | 66 | // Continuous Integration mode 67 | // if true, Karma captures browsers, runs the tests and exits 68 | singleRun: true 69 | }); 70 | }; 71 | -------------------------------------------------------------------------------- /lib/pusher-angular.min.js: -------------------------------------------------------------------------------- 1 | "use strict";angular.module("pusher-angular",[]).factory("$pusher",["$rootScope","$channel","$connection",function(n,e,i){function t(n){if(!(this instanceof t)){return new t(n)}this._assertValidClient(n);this.client=n;this.connection=i(n.connection,n);this.channels={}}t.prototype={subscribe:function(n){var i=this.client.channel(n);if(i===undefined){i=this.client.subscribe(n)}i=e(i,this);this.channels[n]=i;return i},unsubscribe:function(n){if(this.client.channel(n)){this.client.unsubscribe(n);if(this.channels[n]){delete this.channels[n]}}},bind:function(e,i,t,s){var r=angular.isDefined(s)&&!s,o=function(e){i(e);if(!r)n.$digest()};this.client.bind(e,o,t);return o},bind_global:function(e,i){var t=angular.isDefined(i)&&!i;this.client.bind_global(function(i,s){e(i,s);if(!t)n.$digest()})},unbind:function(n,e,i){this.client.unbind(n,e,i)},disconnect:function(){this.client.disconnect()},channel:function(n){return this.channels[n]},allChannels:function(){return this.channels},_assertValidClient:function(n){if(!angular.isObject(n)||!angular.isObject(n.connection)||typeof n.channel!=="function"){throw new Error("Invalid Pusher client object")}}};return t}]).factory("$channel",["$rootScope","$members",function(n,e){function i(n){if(n.indexOf("presence-")==-1&&n.indexOf("private-")==-1){throw new Error("Presence or private channel required")}}function t(n,i){if(!(this instanceof t)){return new t(n,i)}this._assertValidChannel(n);this.baseChannel=n;this.client=i;this.name=n.name;if(n.name.indexOf("presence")==-1){this.members=function(){throw new Error("Members object only exists for presence channels")}}else{this.members=e(n.members,n)}}t.prototype={bind:function(e,i,t,s){var r=angular.isDefined(s)&&!s,o=function(e){i(e);if(!r)n.$digest()};this.baseChannel.bind(e,o,t);return o},unbind:function(n,e,i){this.baseChannel.unbind(n,e,i)},bind_global:function(e,i){var t=angular.isDefined(i)&&!i;this.baseChannel.bind_global(function(i,s){e(i,s);if(!t)n.$digest()})},trigger:function(n,e){i(this.name);if(n.indexOf("client-")==-1){throw new Error("Event name requires 'client-' prefix")}return this.baseChannel.trigger(n,e)},_assertValidChannel:function(n){if(!angular.isObject(n)||typeof n.name!=="string"){throw new Error("Invalid Pusher channel object")}}};return t}]).factory("$members",["$rootScope",function(n){function e(i,t){if(!(this instanceof e)){return new e(i,t)}var s=this;this._assertValidMembers(i);this.baseMembers=i;this.baseChannel=t;this.me={};this.count=0;this.members={};t.bind("pusher:subscription_succeeded",function(e){s.me=e.me;s.count=e.count;s.members=e.members;n.$digest()});t.bind("pusher:member_added",function(e){s.count++;if(e.info){s.members[e.id.toString()]=e.info}else{s.members[e.id.toString()]=null}n.$digest()});t.bind("pusher:member_removed",function(e){s.count--;delete s.members[e.id.toString()];n.$digest()})}e.prototype={get:function(n){return this.baseMembers.get(n)},each:function(e){this.baseMembers.each(function(i){e(i);n.$digest()})},_assertValidMembers:function(n){if(!angular.isObject(n)||typeof n.me!=="object"){throw new Error("Invalid Pusher channel members object")}}};return e}]).factory("$connection",["$rootScope",function(n){function e(n,i){if(!(this instanceof e)){return new e(n,i)}this._assertValidConnection(n);this.baseConnection=n;this.baseClient=i}e.prototype={bind:function(e,i,t,s){var r=angular.isDefined(s)&&!s;this.baseConnection.bind(e,function(e){i(e);if(!r)n.$digest()},t)},bind_global:function(e,i){var t=angular.isDefined(i)&&!i;this.baseConnection.bind_global(function(i,s){e(i,s);if(!t)n.$digest()})},_assertValidConnection:function(n){if(!angular.isObject(n)){throw new Error("Invalid Pusher connection object")}}};return e}]); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pusher-angular", 3 | "version": "1.0.0", 4 | "description": "Angular library for using Pusher Channels", 5 | "main": "lib/pusher-angular.js", 6 | "scripts": { 7 | "test": "karma start karma.conf.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/pusher/pusher-angular.git" 12 | }, 13 | "keywords": [ 14 | "pusher", 15 | "angular", 16 | "websockets" 17 | ], 18 | "author": "Hamilton Chapman", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/pusher/pusher-angular/issues" 22 | }, 23 | "homepage": "https://github.com/pusher/pusher-angular", 24 | "devDependencies": { 25 | "coveralls": "^3.0.0", 26 | "jasmine-core": "^2.8.0", 27 | "karma": "^1.7.1", 28 | "karma-chrome-launcher": "^2.2.0", 29 | "karma-cli": "1.0.1", 30 | "karma-coverage": "^1.1.1", 31 | "karma-firefox-launcher": "^1.0.1", 32 | "karma-jasmine": "^1.1.1", 33 | "karma-phantomjs-launcher": "^1.0.4", 34 | "karma-spec-reporter": "^0.0.31", 35 | "uglify-js": "^3.2.1" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /public/components/angular-mocks/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-mocks", 3 | "version": "1.3.15", 4 | "main": "./angular-mocks.js", 5 | "ignore": [], 6 | "dependencies": { 7 | "angular": "1.3.15" 8 | }, 9 | "homepage": "https://github.com/angular/bower-angular-mocks", 10 | "_release": "1.3.15", 11 | "_resolution": { 12 | "type": "version", 13 | "tag": "v1.3.15", 14 | "commit": "1ffdfef850b10d40f2838c1bba41a95755c2d8da" 15 | }, 16 | "_source": "git://github.com/angular/bower-angular-mocks.git", 17 | "_target": "*", 18 | "_originalSource": "angular-mocks" 19 | } -------------------------------------------------------------------------------- /public/components/angular-mocks/README.md: -------------------------------------------------------------------------------- 1 | # packaged angular-mocks 2 | 3 | This repo is for distribution on `npm` and `bower`. The source for this module is in the 4 | [main AngularJS repo](https://github.com/angular/angular.js/tree/master/src/ngMock). 5 | Please file issues and pull requests against that repo. 6 | 7 | ## Install 8 | 9 | You can install this package either with `npm` or with `bower`. 10 | 11 | ### npm 12 | 13 | ```shell 14 | npm install angular-mocks 15 | ``` 16 | 17 | You can `require` ngMock modules: 18 | 19 | ```js 20 | var angular = require('angular'); 21 | angular.module('myMod', [ 22 | require('angular-animate'), 23 | require('angular-mocks/ngMock') 24 | require('angular-mocks/ngAnimateMock') 25 | ]); 26 | ``` 27 | 28 | ### bower 29 | 30 | ```shell 31 | bower install angular-mocks 32 | ``` 33 | 34 | The mocks are then available at `bower_components/angular-mocks/angular-mocks.js`. 35 | 36 | ## Documentation 37 | 38 | Documentation is available on the 39 | [AngularJS docs site](https://docs.angularjs.org/guide/unit-testing). 40 | 41 | ## License 42 | 43 | The MIT License 44 | 45 | Copyright (c) 2010-2015 Google, Inc. http://angularjs.org 46 | 47 | Permission is hereby granted, free of charge, to any person obtaining a copy 48 | of this software and associated documentation files (the "Software"), to deal 49 | in the Software without restriction, including without limitation the rights 50 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 51 | copies of the Software, and to permit persons to whom the Software is 52 | furnished to do so, subject to the following conditions: 53 | 54 | The above copyright notice and this permission notice shall be included in 55 | all copies or substantial portions of the Software. 56 | 57 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 58 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 59 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 60 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 61 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 62 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 63 | THE SOFTWARE. 64 | -------------------------------------------------------------------------------- /public/components/angular-mocks/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-mocks", 3 | "version": "1.3.15", 4 | "main": "./angular-mocks.js", 5 | "ignore": [], 6 | "dependencies": { 7 | "angular": "1.3.15" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /public/components/angular-mocks/ngAnimateMock.js: -------------------------------------------------------------------------------- 1 | require('./angular-mocks'); 2 | module.exports = 'ngAnimateMock'; 3 | -------------------------------------------------------------------------------- /public/components/angular-mocks/ngMock.js: -------------------------------------------------------------------------------- 1 | require('./angular-mocks'); 2 | module.exports = 'ngMock'; 3 | -------------------------------------------------------------------------------- /public/components/angular-mocks/ngMockE2E.js: -------------------------------------------------------------------------------- 1 | require('./angular-mocks'); 2 | module.exports = 'ngMockE2E'; 3 | -------------------------------------------------------------------------------- /public/components/angular-mocks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-mocks", 3 | "version": "1.3.15", 4 | "description": "AngularJS mocks for testing", 5 | "main": "angular-mocks.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/angular/angular.js.git" 12 | }, 13 | "keywords": [ 14 | "angular", 15 | "framework", 16 | "browser", 17 | "mocks", 18 | "testing", 19 | "client-side" 20 | ], 21 | "author": "Angular Core Team ", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/angular/angular.js/issues" 25 | }, 26 | "homepage": "http://angularjs.org" 27 | } 28 | -------------------------------------------------------------------------------- /public/components/angular/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular", 3 | "version": "1.3.15", 4 | "main": "./angular.js", 5 | "ignore": [], 6 | "dependencies": {}, 7 | "homepage": "https://github.com/angular/bower-angular", 8 | "_release": "1.3.15", 9 | "_resolution": { 10 | "type": "version", 11 | "tag": "v1.3.15", 12 | "commit": "ba7abcfa409ba852146e6ba206693cf7bac3e359" 13 | }, 14 | "_source": "git://github.com/angular/bower-angular.git", 15 | "_target": "1.3.15", 16 | "_originalSource": "angular" 17 | } -------------------------------------------------------------------------------- /public/components/angular/README.md: -------------------------------------------------------------------------------- 1 | # packaged angular 2 | 3 | This repo is for distribution on `npm` and `bower`. The source for this module is in the 4 | [main AngularJS repo](https://github.com/angular/angular.js). 5 | Please file issues and pull requests against that repo. 6 | 7 | ## Install 8 | 9 | You can install this package either with `npm` or with `bower`. 10 | 11 | ### npm 12 | 13 | ```shell 14 | npm install angular 15 | ``` 16 | 17 | Then add a ` 21 | ``` 22 | 23 | Or `require('angular')` from your code. 24 | 25 | ### bower 26 | 27 | ```shell 28 | bower install angular 29 | ``` 30 | 31 | Then add a ` 35 | ``` 36 | 37 | ## Documentation 38 | 39 | Documentation is available on the 40 | [AngularJS docs site](http://docs.angularjs.org/). 41 | 42 | ## License 43 | 44 | The MIT License 45 | 46 | Copyright (c) 2010-2015 Google, Inc. http://angularjs.org 47 | 48 | Permission is hereby granted, free of charge, to any person obtaining a copy 49 | of this software and associated documentation files (the "Software"), to deal 50 | in the Software without restriction, including without limitation the rights 51 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 52 | copies of the Software, and to permit persons to whom the Software is 53 | furnished to do so, subject to the following conditions: 54 | 55 | The above copyright notice and this permission notice shall be included in 56 | all copies or substantial portions of the Software. 57 | 58 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 59 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 60 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 61 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 62 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 63 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 64 | THE SOFTWARE. 65 | -------------------------------------------------------------------------------- /public/components/angular/angular-csp.css: -------------------------------------------------------------------------------- 1 | /* Include this file in your html if you are using the CSP mode. */ 2 | 3 | @charset "UTF-8"; 4 | 5 | [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], 6 | .ng-cloak, .x-ng-cloak, 7 | .ng-hide:not(.ng-hide-animate) { 8 | display: none !important; 9 | } 10 | 11 | ng\:form { 12 | display: block; 13 | } 14 | -------------------------------------------------------------------------------- /public/components/angular/angular.min.js.gzip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pusher/pusher-angular/26f48e0ed23e40f63cb8faa8b7a8bc70a716d95a/public/components/angular/angular.min.js.gzip -------------------------------------------------------------------------------- /public/components/angular/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular", 3 | "version": "1.3.15", 4 | "main": "./angular.js", 5 | "ignore": [], 6 | "dependencies": { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /public/components/angular/index.js: -------------------------------------------------------------------------------- 1 | require('./angular'); 2 | module.exports = angular; 3 | -------------------------------------------------------------------------------- /public/components/angular/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular", 3 | "version": "1.3.15", 4 | "description": "HTML enhanced for web apps", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/angular/angular.js.git" 12 | }, 13 | "keywords": [ 14 | "angular", 15 | "framework", 16 | "browser", 17 | "client-side" 18 | ], 19 | "author": "Angular Core Team ", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/angular/angular.js/issues" 23 | }, 24 | "homepage": "http://angularjs.org" 25 | } 26 | --------------------------------------------------------------------------------