├── .gitignore
├── .vimrc
├── Gruntfile.coffee
├── README.md
├── bower.json
├── dist
├── page_visibility.js
└── page_visibility.min.js
├── js
├── page_visibility.js
└── page_visibility.js.map
├── karma.conf.coffee
├── package.json
├── spec
└── page_visibility.spec.coffee
├── src
└── page_visibility.coffee
└── todos.md
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | bower_components
3 | node_modules
4 |
--------------------------------------------------------------------------------
/.vimrc:
--------------------------------------------------------------------------------
1 | set wildignore+=*node_modules*,*bower_components*
2 |
--------------------------------------------------------------------------------
/Gruntfile.coffee:
--------------------------------------------------------------------------------
1 | module.exports = (grunt)->
2 | grunt.loadNpmTasks 'grunt-contrib-coffee'
3 | grunt.loadNpmTasks 'grunt-contrib-watch'
4 | grunt.loadNpmTasks 'grunt-karma'
5 | grunt.loadNpmTasks 'grunt-contrib-uglify'
6 | grunt.loadNpmTasks 'grunt-contrib-copy'
7 |
8 | grunt.initConfig
9 | coffee:
10 | compile:
11 | options:
12 | sourceMap: true
13 | watch: true
14 | files: [
15 | expand: true
16 | cwd: 'src/'
17 | src: '**/*.coffee'
18 | dest: 'js/'
19 | ext: '.js'
20 | ]
21 | watch:
22 | source:
23 | files: [ 'src/**/*' ]
24 | tasks: [ 'coffee' ]
25 |
26 | karma:
27 | unit:
28 | configFile: 'karma.conf.coffee'
29 | single:
30 | configFile: 'karma.conf.coffee'
31 | options:
32 | singleRun: true
33 |
34 | uglify:
35 | source:
36 | expand: true
37 | cwd: 'js/'
38 | src: '**/*.js'
39 | dest: 'dist/'
40 | ext: '.min.js'
41 |
42 | copy:
43 | source:
44 | expand: true
45 | cwd: 'js/'
46 | src: '**/*.js'
47 | dest: 'dist/'
48 |
49 |
50 |
51 | grunt.registerTask 'dev', [ 'coffee', 'watch' ]
52 | grunt.registerTask 'build', [ 'karma:single', 'coffee', 'copy', 'uglify' ]
53 | grunt.registerTask 'default', [ 'dev' ]
54 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Angular Page Visibility: a Page Visibility API interface for Angular
2 | `angular-page-visibility` is a tiny lib which integrate [Page Visibility API](https://developer.mozilla.org/en-US/docs/Web/Guide/User_experience/Using_the_Page_Visibility_API) with Angular.
3 |
4 | It is exposed as a scope, which `$broadcast`-s `pageFocused` and `pageBlurred` when page is focused / blurred.
5 | For old browsers [not supporting page visibility API](http://caniuse.com/#feat=pagevisibility), it ignores it silently.
6 |
7 | ## Usage
8 | To use `angular-page-visibility`, just inject it, then listen to the events.
9 |
10 | ```javascript
11 | angular.module('app')
12 | .controller('MyController', function($scope, $pageVisibility) {
13 | $pageVisibility.$on('pageFocused', function(){
14 | // page is focused
15 | });
16 |
17 | $pageVisibility.$on('pageBlurred', function(){
18 | // page is blurred
19 | });
20 | });
21 | ```
22 |
23 | ## Installation
24 |
25 | 1) include script: script can be included via `bower` or downloading directly
26 |
27 | - via bower:
28 | `$ bower install angular-page-visibility`
29 |
30 | - download directly
31 | ```html
32 |
33 | ```
34 |
35 | 2) include the module:
36 |
37 | ```javascript
38 | angular.app('myApp', [ 'angular-page-visibility' ])
39 |
40 | ```
41 |
42 | ## Testing
43 | to test `angular-page-visibility`, `grunt`, `karma` are needed.
44 |
45 | 1. `$ npm install`
46 | 2. `$ bower install`
47 | 3. `$ npm test`
48 |
49 | ## Licence:
50 | MIT
51 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular_page_visibility",
3 | "version": "0.0.4",
4 | "main": "./dist/page_visibility.min.js",
5 | "authors": [
6 | "Yang-Hsing Lin "
7 | ],
8 | "description": "angularjs interface of page visibility API",
9 | "license": "MIT",
10 | "ignore": [
11 | "**/.*",
12 | "node_modules",
13 | "bower_components",
14 | "test",
15 | "tests"
16 | ],
17 | "dependencies": {},
18 | "devDependencies": {
19 | "angular": "1.2.22",
20 | "angular-mocks": "1.2.22",
21 | "sinonjs": "~1.10.2"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/dist/page_visibility.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | angular.module('angular-page-visibility', []).factory('$pageVisibility', [
3 | '$rootScope', '$document', function($rootScope, $document) {
4 | var getVisibilityKeys, hiddenKey, pageVisibility, visibilityChagedKey, _ref;
5 | pageVisibility = $rootScope.$new();
6 | getVisibilityKeys = function() {
7 | if (typeof ($document.prop('hidden')) !== 'undefined') {
8 | return ['hidden', 'visibilitychange'];
9 | } else if (typeof ($document.prop('mozHidden')) !== 'undefined') {
10 | return ['mozHidden', 'mozvisibilitychange'];
11 | } else if (typeof ($document.prop('msHidden')) !== 'undefined') {
12 | return ['msHidden', 'msvisibilitychange'];
13 | } else if (typeof ($document.prop('webkitHidden')) !== 'undefined') {
14 | return ['webkitHidden', 'webkitvisibilitychange'];
15 | }
16 | };
17 | if (!getVisibilityKeys()) {
18 | return pageVisibility;
19 | }
20 | _ref = getVisibilityKeys(), hiddenKey = _ref[0], visibilityChagedKey = _ref[1];
21 | $document.on(visibilityChagedKey, function() {
22 | if ($document.prop(hiddenKey)) {
23 | return pageVisibility.$broadcast('pageBlurred');
24 | } else {
25 | return pageVisibility.$broadcast('pageFocused');
26 | }
27 | });
28 | return pageVisibility;
29 | }
30 | ]);
31 |
32 | }).call(this);
33 |
34 | //# sourceMappingURL=page_visibility.js.map
35 |
--------------------------------------------------------------------------------
/dist/page_visibility.min.js:
--------------------------------------------------------------------------------
1 | (function(){angular.module("angular-page-visibility",[]).factory("$pageVisibility",["$rootScope","$document",function(a,b){var c,d,e,f,g;return e=a.$new(),(c=function(){return"undefined"!=typeof b.prop("hidden")?["hidden","visibilitychange"]:"undefined"!=typeof b.prop("mozHidden")?["mozHidden","mozvisibilitychange"]:"undefined"!=typeof b.prop("msHidden")?["msHidden","msvisibilitychange"]:"undefined"!=typeof b.prop("webkitHidden")?["webkitHidden","webkitvisibilitychange"]:void 0})()?(g=c(),d=g[0],f=g[1],b.on(f,function(){return e.$broadcast(b.prop(d)?"pageBlurred":"pageFocused")}),e):e}])}).call(this);
--------------------------------------------------------------------------------
/js/page_visibility.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | angular.module('angular-page-visibility', []).factory('$pageVisibility', [
3 | '$rootScope', '$document', function($rootScope, $document) {
4 | var getVisibilityKeys, hiddenKey, pageVisibility, visibilityChagedKey, _ref;
5 | pageVisibility = $rootScope.$new();
6 | getVisibilityKeys = function() {
7 | if (typeof ($document.prop('hidden')) !== 'undefined') {
8 | return ['hidden', 'visibilitychange'];
9 | } else if (typeof ($document.prop('mozHidden')) !== 'undefined') {
10 | return ['mozHidden', 'mozvisibilitychange'];
11 | } else if (typeof ($document.prop('msHidden')) !== 'undefined') {
12 | return ['msHidden', 'msvisibilitychange'];
13 | } else if (typeof ($document.prop('webkitHidden')) !== 'undefined') {
14 | return ['webkitHidden', 'webkitvisibilitychange'];
15 | }
16 | };
17 | if (!getVisibilityKeys()) {
18 | return pageVisibility;
19 | }
20 | _ref = getVisibilityKeys(), hiddenKey = _ref[0], visibilityChagedKey = _ref[1];
21 | $document.on(visibilityChagedKey, function() {
22 | if ($document.prop(hiddenKey)) {
23 | return pageVisibility.$broadcast('pageBlurred');
24 | } else {
25 | return pageVisibility.$broadcast('pageFocused');
26 | }
27 | });
28 | return pageVisibility;
29 | }
30 | ]);
31 |
32 | }).call(this);
33 |
34 | //# sourceMappingURL=page_visibility.js.map
35 |
--------------------------------------------------------------------------------
/js/page_visibility.js.map:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "file": "page_visibility.js",
4 | "sourceRoot": "../src/",
5 | "sources": [
6 | "page_visibility.coffee"
7 | ],
8 | "names": [],
9 | "mappings": "AAAA;AAAA,EAAA,OAAO,CAAC,MAAR,CAAe,yBAAf,EAA0C,EAA1C,CACE,CAAC,OADH,CACW,iBADX,EAC8B;IAAE,YAAF,EAAgB,WAAhB,EAA6B,SAAC,UAAD,EAAa,SAAb,GAAA;AACvD,UAAA,uEAAA;AAAA,MAAA,cAAA,GAAiB,UAAU,CAAC,IAAX,CAAA,CAAjB,CAAA;AAAA,MAEA,iBAAA,GAAoB,SAAA,GAAA;AAClB,QAAA,IAAG,MAAA,CAAA,CAAO,SAAS,CAAC,IAAV,CAAe,QAAf,CAAD,CAAN,KAAoC,WAAvC;iBACE,CAAE,QAAF,EAAY,kBAAZ,EADF;SAAA,MAEK,IAAG,MAAA,CAAA,CAAO,SAAS,CAAC,IAAV,CAAe,WAAf,CAAD,CAAN,KAAuC,WAA1C;iBACH,CAAE,WAAF,EAAe,qBAAf,EADG;SAAA,MAEA,IAAG,MAAA,CAAA,CAAO,SAAS,CAAC,IAAV,CAAe,UAAf,CAAD,CAAN,KAAsC,WAAzC;iBACH,CAAE,UAAF,EAAc,oBAAd,EADG;SAAA,MAEA,IAAG,MAAA,CAAA,CAAO,SAAS,CAAC,IAAV,CAAe,cAAf,CAAD,CAAN,KAA0C,WAA7C;iBACH,CAAE,cAAF,EAAkB,wBAAlB,EADG;SAPa;MAAA,CAFpB,CAAA;AAYA,MAAA,IAAA,CAAA,iBAA6B,CAAA,CAA7B;AAAA,eAAO,cAAP,CAAA;OAZA;AAAA,MAcA,OAAmC,iBAAA,CAAA,CAAnC,EAAC,mBAAD,EAAY,6BAdZ,CAAA;AAAA,MAgBA,SAAS,CAAC,EAAV,CAAa,mBAAb,EAAkC,SAAA,GAAA;AAChC,QAAA,IAAG,SAAS,CAAC,IAAV,CAAe,SAAf,CAAH;iBACE,cAAc,CAAC,UAAf,CAA0B,aAA1B,EADF;SAAA,MAAA;iBAGE,cAAc,CAAC,UAAf,CAA0B,aAA1B,EAHF;SADgC;MAAA,CAAlC,CAhBA,CAAA;aAuBA,eAxBuD;IAAA,CAA7B;GAD9B,CAAA,CAAA;AAAA"
10 | }
--------------------------------------------------------------------------------
/karma.conf.coffee:
--------------------------------------------------------------------------------
1 | # Karma configuration
2 | # Generated on Mon Aug 25 2014 17:15:24 GMT+0800 (CST)
3 |
4 | module.exports = (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 | 'bower_components/angular/angular.js',
19 | 'bower_components/angular-mocks/angular-mocks.js',
20 | 'bower_components/sinonjs/sinon.js',
21 | 'src/**/*.coffee',
22 | 'spec/**/*.coffee'
23 | ]
24 |
25 |
26 | # list of files to exclude
27 | exclude: [
28 | '**/*.swp'
29 | ]
30 |
31 |
32 | # preprocess matching files before serving them to the browser
33 | # available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
34 | preprocessors: {
35 | '**/*.coffee': ['coffee']
36 | }
37 |
38 | coffeePreprocessor:
39 | options:
40 | sourceMap: true
41 |
42 |
43 | # test results reporter to use
44 | # possible values: 'dots', 'progress'
45 | # available reporters: https://npmjs.org/browse/keyword/karma-reporter
46 | reporters: ['progress']
47 |
48 |
49 | # web server port
50 | port: 9876
51 |
52 |
53 | # enable / disable colors in the output (reporters and logs)
54 | colors: true
55 |
56 |
57 | # level of logging
58 | # possible values:
59 | # - config.LOG_DISABLE
60 | # - config.LOG_ERROR
61 | # - config.LOG_WARN
62 | # - config.LOG_INFO
63 | # - config.LOG_DEBUG
64 | logLevel: config.LOG_INFO
65 |
66 |
67 | # enable / disable watching file and executing tests whenever any file changes
68 | autoWatch: true
69 |
70 |
71 | # start these browsers
72 | # available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
73 | browsers: ['Chrome']
74 |
75 |
76 | # Continuous Integration mode
77 | # if true, Karma captures browsers, runs the tests and exits
78 | singleRun: false
79 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular_page_visibility",
3 | "version": "0.0.4",
4 | "description": "angularjs interface of page visibility API",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "grunt karma:single"
8 | },
9 | "devDependencies": {
10 | "coffee-script": "^1.7.1",
11 | "grunt": "^0.4.5",
12 | "grunt-concurrent": "^0.5.0",
13 | "grunt-contrib-coffee": "~0.11.1",
14 | "grunt-contrib-copy": "^0.5.0",
15 | "grunt-contrib-uglify": "^0.5.1",
16 | "grunt-contrib-watch": "^0.6.1",
17 | "grunt-karma": "^0.8.3",
18 | "karma": "~0.12.22",
19 | "karma-chrome-launcher": "~0.1.4",
20 | "karma-coffee-preprocessor": "^0.2.1",
21 | "karma-jasmine": "~0.1.5"
22 | },
23 | "author": [
24 | { "name": "Yang-Hsing Lin", "email": "yanghsing.lin@gmail.com" }
25 | ],
26 | "repository": {
27 | "type": "git",
28 | "url": "https://github.com/mz026/angular_page_visibility.git"
29 | },
30 | "license": "MIT"
31 | }
32 |
--------------------------------------------------------------------------------
/spec/page_visibility.spec.coffee:
--------------------------------------------------------------------------------
1 | describe 'Page Visibility', ->
2 | $document = null
3 | beforeEach module 'angular-page-visibility'
4 | beforeEach module ($provide)->
5 | $document =
6 | attrs: {}
7 | events: {}
8 | _setAttr: (key, val)->
9 | @attrs[key] = val
10 | attr: (key)->
11 | @attrs[key]
12 | prop: (key)->
13 | @attrs[key]
14 | on: (eventName, cb)->
15 | @events[eventName] = cb
16 | _trigger: (eventName)->
17 | @events[eventName] && @events[eventName]()
18 |
19 | $provide.value('$document', $document)
20 | null
21 |
22 | ensurePageVisibilityEventsWith = (hiddenKey, visibilityChagedKey)->
23 | describe "when document visibility is controlled by `#{hiddenKey}`", ->
24 | it '$broadcast-s `pageFocused` when page turn visible', ->
25 | inject (_$document_)->
26 | $document._setAttr(hiddenKey, false)
27 |
28 | inject ($pageVisibility)->
29 | onFocused = sinon.spy()
30 | $pageVisibility.$on('pageFocused', onFocused)
31 |
32 | $document._trigger(visibilityChagedKey)
33 |
34 | expect(onFocused.called).toBe(true)
35 |
36 | it '$broadcast-s `pageBlurred` when page turns invisible', ->
37 | inject (_$document_)->
38 | $document._setAttr(hiddenKey, true)
39 |
40 | inject ($pageVisibility)->
41 | onBlurred = sinon.spy()
42 | $pageVisibility.$on('pageBlurred', onBlurred)
43 |
44 | $document._trigger(visibilityChagedKey)
45 |
46 | expect(onBlurred.called).toBe(true)
47 |
48 | ensurePageVisibilityEventsWith('hidden', 'visibilitychange')
49 | ensurePageVisibilityEventsWith('mozHidden', 'mozvisibilitychange')
50 | ensurePageVisibilityEventsWith('msHidden', 'msvisibilitychange')
51 | ensurePageVisibilityEventsWith('webkitHidden', 'webkitvisibilitychange')
52 |
53 | it 'does nothing if the browser does not support page visibility API', ->
54 | inject ($pageVisibility)->
55 |
56 |
--------------------------------------------------------------------------------
/src/page_visibility.coffee:
--------------------------------------------------------------------------------
1 | angular.module('angular-page-visibility', [])
2 | .factory('$pageVisibility', [ '$rootScope', '$document', ($rootScope, $document)->
3 | pageVisibility = $rootScope.$new()
4 |
5 | getVisibilityKeys = ->
6 | if typeof($document.prop('hidden')) != 'undefined'
7 | [ 'hidden', 'visibilitychange' ]
8 | else if typeof($document.prop('mozHidden')) != 'undefined'
9 | [ 'mozHidden', 'mozvisibilitychange' ]
10 | else if typeof($document.prop('msHidden')) != 'undefined'
11 | [ 'msHidden', 'msvisibilitychange' ]
12 | else if typeof($document.prop('webkitHidden')) != 'undefined'
13 | [ 'webkitHidden', 'webkitvisibilitychange' ]
14 |
15 | return pageVisibility unless getVisibilityKeys()
16 |
17 | [hiddenKey, visibilityChagedKey] = getVisibilityKeys()
18 |
19 | $document.on(visibilityChagedKey, ->
20 | if $document.prop(hiddenKey)
21 | pageVisibility.$broadcast('pageBlurred')
22 | else
23 | pageVisibility.$broadcast('pageFocused')
24 | )
25 |
26 | pageVisibility
27 | ])
28 |
--------------------------------------------------------------------------------
/todos.md:
--------------------------------------------------------------------------------
1 | - create `Gruntfile` to
2 |
3 | 1. `grunt serve`:
4 | - watch / compile coffee script
5 | - run tests
6 |
7 | 2. `grunt build`:
8 | - minify script to `dist/`
9 |
--------------------------------------------------------------------------------