visibilityjs 0.4.5 → 0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/ChangeLog CHANGED
@@ -1,3 +1,8 @@
1
+ == 0.5 (SCORE, communication)
2
+ * Split library to core and timers modules.
3
+ * Allow to unbind change listener.
4
+ * Use common logic in change(), afterPrerendering() and onVisible().
5
+
1
6
  == 0.4.5 (Pioneer 3, closer)
2
7
  * Fix gem assets directory.
3
8
  * Update development dependencies.
data/README.md CHANGED
@@ -219,6 +219,18 @@ Visibility.change(function (e, state) {
219
219
 
220
220
  ## Installing
221
221
 
222
+ ### Packages
223
+
224
+ Visibility.js shipped with 4 files:
225
+
226
+ * `visibility.core` – core module.
227
+ * `visibility.timers` – `every` and `stop` methods to set `setTimeout` depend
228
+ on visibility state.
229
+ * `visibility` – `visibility.core` and `visibility.timers` together.
230
+ * `visibility.fallback` – fallback for browser without Page Visibility API.
231
+ It use document `focus`/`blur` events, so it say, that document in hidden,
232
+ when browser just lose focus.
233
+
222
234
  ### Ruby on Rails
223
235
 
224
236
  For Ruby on Rails you can use gem for Assets Pipeline.
@@ -241,6 +253,13 @@ For Ruby on Rails you can use gem for Assets Pipeline.
241
253
  #= require visibility
242
254
  ```
243
255
 
256
+ If you didn’t use `every` method, you can reduce library size, by including
257
+ only core module:
258
+
259
+ ```coffee
260
+ #= require visibility.core
261
+ ```
262
+
244
263
  ### Other
245
264
 
246
265
  If you don’t use any assets packaging manager (it’s very bad idea), you can use
@@ -0,0 +1,248 @@
1
+ /*
2
+ * Copyright 2011 Andrey “A.I.” Sitnik <andrey@sitnik.ru>,
3
+ * sponsored by Evil Martians.
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU Lesser General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU Lesser General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Lesser General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ ;(function () {
20
+ "use strict";
21
+
22
+ var defined = function (variable) {
23
+ return ('undefined' != typeof(variable));
24
+ };
25
+
26
+ // Visibility.js allow you to know, that your web page is in the background
27
+ // tab and thus not visible to the user. This library is wrap under
28
+ // Page Visibility API. It fix problems with different vendor prefixes and
29
+ // add high-level useful functions.
30
+ window.Visibility = {
31
+
32
+ // Call callback only when page become to visible for user or
33
+ // call it now if page is visible now or Page Visibility API
34
+ // doesn’t supported.
35
+ //
36
+ // Return true if callback if called now.
37
+ //
38
+ // Visibility.onVisible(function () {
39
+ // Notification.animateNotice("Hello");
40
+ // });
41
+ onVisible: function (callback) {
42
+ if ( !this.isSupported() || !this.hidden() ) {
43
+ callback();
44
+ return true;
45
+ }
46
+
47
+ var listener = this.change(function (e, state) {
48
+ if ( !Visibility.hidden() ) {
49
+ Visibility.unbind(listener);
50
+ callback();
51
+ }
52
+ });
53
+ },
54
+
55
+ // Call callback when visibility will be changed. First argument for
56
+ // callback will be original event object, second will be visibility
57
+ // state name.
58
+ //
59
+ // Return listener ID to unbind listener by `unbind` method.
60
+ //
61
+ // If Page Visibility API doesn’t supported method will be return false
62
+ // and callback never will be called.
63
+ //
64
+ // Visibility.change(function(e, state) {
65
+ // Statistics.visibilityChange(state);
66
+ // });
67
+ //
68
+ // It is just proxy to `visibilitychange` event, but use vendor prefix.
69
+ change: function (callback) {
70
+ if ( !this.isSupported() ) {
71
+ return false;
72
+ }
73
+ this._lastCallback += 1;
74
+ var number = this._lastCallback;
75
+ this._callbacks[number] = callback;
76
+ this._setListener();
77
+ return number;
78
+ },
79
+
80
+ // Remove `change` listener by it ID.
81
+ //
82
+ // var id = Visibility.change(function(e, state) {
83
+ // firstChangeCallback();
84
+ // Visibility.unbind(id);
85
+ // });
86
+ unbind: function (id) {
87
+ delete this._callbacks[id];
88
+ },
89
+
90
+ // Call `callback` in any state, expect “prerender”. If current state
91
+ // is “prerender” it will wait until state will be changed.
92
+ // If Page Visibility API doesn’t supported, it will call `callback`
93
+ // immediately.
94
+ //
95
+ // Visibility.afterPrerendering(function () {
96
+ // Statistics.countVisitor();
97
+ // });
98
+ afterPrerendering: function (callback) {
99
+ if ( !this.isSupported() || 'prerender' != this.state() ) {
100
+ callback();
101
+ return true;
102
+ }
103
+
104
+ var listener = this.change(function (e, state) {
105
+ if ( 'prerender' != state ) {
106
+ Visibility.unbind(listener);
107
+ callback();
108
+ }
109
+ });
110
+ },
111
+
112
+ // Return true if page now isn’t visible to user.
113
+ //
114
+ // if ( !Visibility.hidden() ) {
115
+ // VideoPlayer.play();
116
+ // }
117
+ //
118
+ // It is just proxy to `document.hidden`, but use vendor prefix.
119
+ hidden: function () {
120
+ return this._prop('hidden', false);
121
+ },
122
+
123
+ // Return visibility state: 'visible', 'hidden' or 'prerender'.
124
+ //
125
+ // if ( 'prerender' == Visibility.state() ) {
126
+ // Statistics.pageIsPrerendering();
127
+ // }
128
+ //
129
+ // Don’t use `Visibility.state()` to detect, is page visible, because
130
+ // visibility states can extend in next API versions.
131
+ // Use more simpler and general `Visibility.hidden()` for this cases.
132
+ //
133
+ // It is just proxy to `document.visibilityState`, but use
134
+ // vendor prefix.
135
+ state: function () {
136
+ return this._prop('visibilityState', 'visible');
137
+ },
138
+
139
+ // Return true if browser support Page Visibility API.
140
+ //
141
+ // if ( Visibility.isSupported() ) {
142
+ // Statistics.startTrackingVisibility();
143
+ // Visibility.change(function(e, state)) {
144
+ // Statistics.trackVisibility(state);
145
+ // });
146
+ // }
147
+ isSupported: function () {
148
+ return defined(this._prefix());
149
+ },
150
+
151
+ // Link to document object to change it in tests.
152
+ _doc: window.document,
153
+
154
+ // Vendor prefixes to create event and properties names.
155
+ _prefixes: ['webkit', 'moz', 'o', 'ms'],
156
+
157
+ // Vendor prefix cached by `_prefix` function.
158
+ _chechedPrefix: null,
159
+
160
+ // Is listener for `visibilitychange` event is already added
161
+ // by `_setListener` method.
162
+ _listening: false,
163
+
164
+ // Last timer number.
165
+ _lastCallback: -1,
166
+
167
+ // Callbacks from `change` method, that wait visibility changes.
168
+ _callbacks: { },
169
+
170
+ // Variable to check hidden-visible state changes.
171
+ _hiddenBefore: false,
172
+
173
+ // Initialize variables on page loading.
174
+ _init: function () {
175
+ this._hiddenBefore = this.hidden();
176
+ },
177
+
178
+ // Detect vendor prefix and return it.
179
+ _prefix: function () {
180
+ if ( null !== this._chechedPrefix ) {
181
+ return this._chechedPrefix;
182
+ }
183
+ if ( defined(this._doc.visibilityState) ) {
184
+ return this._chechedPrefix = '';
185
+ }
186
+ var name;
187
+ for ( var i = 0; i < this._prefixes.length; i++ ) {
188
+ name = this._prefixes[i] + 'VisibilityState';
189
+ if ( defined(this._doc[name]) ) {
190
+ return this._chechedPrefix = this._prefixes[i];
191
+ }
192
+ }
193
+ },
194
+
195
+ // Return property name with vendor prefix.
196
+ _name: function (name) {
197
+ var prefix = this._prefix();
198
+ if ( '' == prefix ) {
199
+ return name;
200
+ } else {
201
+ return prefix +
202
+ name.substr(0, 1).toUpperCase() + name.substr(1);
203
+ }
204
+ },
205
+
206
+ // Return document’s property value with name with vendor prefix.
207
+ // If API is not support, it will retun `unsupported` value.
208
+ _prop: function (name, unsupported) {
209
+ if ( !this.isSupported() ) {
210
+ return unsupported;
211
+ }
212
+ return this._doc[this._name(name)];
213
+ },
214
+
215
+ // Listener for `visibilitychange` event.
216
+ _onChange: function(event) {
217
+ var state = this.state();
218
+
219
+ for ( var i in this._callbacks ) {
220
+ this._callbacks[i].call(this._doc, event, state);
221
+ }
222
+
223
+ this._hiddenBefore = this.hidden();
224
+ },
225
+
226
+ // Set listener for `visibilitychange` event.
227
+ _setListener: function () {
228
+ if ( this._listening ) {
229
+ return;
230
+ }
231
+ var event = this._prefix() + 'visibilitychange';
232
+ var listener = function () {
233
+ Visibility._onChange.apply(Visibility, arguments);
234
+ };
235
+ if ( this._doc.addEventListener ) {
236
+ this._doc.addEventListener(event, listener, false);
237
+ } else {
238
+ this._doc.attachEvent(event, listener);
239
+ }
240
+ this._listening = true;
241
+ this._hiddenBefore = this.hidden();
242
+ }
243
+
244
+ };
245
+
246
+ Visibility._init();
247
+
248
+ })();
@@ -26,7 +26,7 @@
26
26
  //
27
27
  // For Firefox 5–9 it will be better to use MozVisibility hack without
28
28
  // this issue. See <https://github.com/private-face/mozvisibility>.
29
- ;(function() {
29
+ ;(function () {
30
30
  "use strict";
31
31
 
32
32
  if ( document.visibilityState || document.webkitVisibilityState ||
@@ -49,7 +49,7 @@
49
49
  document.dispatchEvent(event);
50
50
  } else {
51
51
  if ( typeof(Visibility) == 'object' ) {
52
- Visibility._onVisibilityChange.call(Visibility, { })
52
+ Visibility._onChange.call(Visibility, { })
53
53
  }
54
54
  }
55
55
  }
@@ -16,10 +16,10 @@
16
16
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
 
19
- ;(function() {
19
+ ;(function () {
20
20
  "use strict";
21
21
 
22
- var defined = function(variable) {
22
+ var defined = function (variable) {
23
23
  return ('undefined' != typeof(variable));
24
24
  };
25
25
 
@@ -35,7 +35,7 @@
35
35
  //
36
36
  // Return true if callback if called now.
37
37
  //
38
- // Visibility.onVisible(function() {
38
+ // Visibility.onVisible(function () {
39
39
  // Notification.animateNotice("Hello");
40
40
  // });
41
41
  onVisible: function (callback) {
@@ -43,84 +43,21 @@
43
43
  callback();
44
44
  return true;
45
45
  }
46
- this._onVisibleCallbacks.push(callback);
47
- this._setListener();
48
- },
49
46
 
50
- // Run callback every `interval` milliseconds if page is visible and
51
- // every `hiddenInterval` milliseconds if page is hidden.
52
- //
53
- // Visibility.every(60 * 1000, 5 * 60 * 1000, function() {
54
- // checkNewMails();
55
- // });
56
- //
57
- // You can skip `hiddenInterval` and callback will be called only if
58
- // page is visible.
59
- //
60
- // Visibility.every(1000, function() {
61
- // updateCountdown();
62
- // });
63
- //
64
- // It is analog of `setInterval(callback, interval)` but use visibility
65
- // state.
66
- //
67
- // It return timer ID, that you can use in `Visibility.stop(id)` to stop
68
- // timer (`clearInterval` analog).
69
- // Warning: timer ID is different from intervalID from `setInterval`,
70
- // so don’t use it in `clearInterval`.
71
- //
72
- // On change state from hidden to visible timers will be execute.
73
- //
74
- // If you include jQuery Chrono plugin before Visibility.js, you could
75
- // use Chrono’s syntax sugar in interval arguments:
76
- //
77
- // Visibility.every('second', function() {
78
- // updateCountdown();
79
- // });
80
- // Visibility.every('1 minute', '5 minutes', function() {
81
- // checkNewMails();
82
- // });
83
- every: function (interval, hiddenInterval, callback) {
84
- if ( !defined(callback) ) {
85
- callback = hiddenInterval;
86
- hiddenInterval = null;
87
- }
88
- this._lastTimer += 1;
89
- var number = this._lastTimer;
90
- this._timers[number] = ({
91
- interval: interval,
92
- hiddenInterval: hiddenInterval,
93
- callback: callback
47
+ var listener = this.change(function (e, state) {
48
+ if ( !Visibility.hidden() ) {
49
+ Visibility.unbind(listener);
50
+ callback();
51
+ }
94
52
  });
95
- this._runTimer(number, false);
96
- if ( this.isSupported() ) {
97
- this._setListener();
98
- }
99
- return number;
100
- },
101
-
102
- // Stop timer from `every` method by it ID (`every` method return it).
103
- //
104
- // slideshow = Visibility.every(5 * 1000, function() {
105
- // changeSlide();
106
- // });
107
- // $('.stopSlideshow').click(function() {
108
- // Visibility.stop(slideshow);
109
- // });
110
- stop: function(id) {
111
- var timer = this._timers[id]
112
- if ( !defined(timer) ) {
113
- return false;
114
- }
115
- this._stopTimer(id);
116
- delete this._timers[id];
117
- return timer;
118
53
  },
119
54
 
120
55
  // Call callback when visibility will be changed. First argument for
121
56
  // callback will be original event object, second will be visibility
122
57
  // state name.
123
58
  //
59
+ // Return listener ID to unbind listener by `unbind` method.
60
+ //
124
61
  // If Page Visibility API doesn’t supported method will be return false
125
62
  // and callback never will be called.
126
63
  //
@@ -133,9 +70,21 @@
133
70
  if ( !this.isSupported() ) {
134
71
  return false;
135
72
  }
136
- this._changeCallbacks.push(callback);
73
+ this._lastCallback += 1;
74
+ var number = this._lastCallback;
75
+ this._callbacks[number] = callback;
137
76
  this._setListener();
138
- return true;
77
+ return number;
78
+ },
79
+
80
+ // Remove `change` listener by it ID.
81
+ //
82
+ // var id = Visibility.change(function(e, state) {
83
+ // firstChangeCallback();
84
+ // Visibility.unbind(id);
85
+ // });
86
+ unbind: function (id) {
87
+ delete this._callbacks[id];
139
88
  },
140
89
 
141
90
  // Call `callback` in any state, expect “prerender”. If current state
@@ -151,8 +100,13 @@
151
100
  callback();
152
101
  return true;
153
102
  }
154
- this._afterPrerenderingCallbacks.push(callback);
155
- this._setListener();
103
+
104
+ var listener = this.change(function (e, state) {
105
+ if ( 'prerender' != state ) {
106
+ Visibility.unbind(listener);
107
+ callback();
108
+ }
109
+ });
156
110
  },
157
111
 
158
112
  // Return true if page now isn’t visible to user.
@@ -163,10 +117,7 @@
163
117
  //
164
118
  // It is just proxy to `document.hidden`, but use vendor prefix.
165
119
  hidden: function () {
166
- if ( !this.isSupported() ) {
167
- return false;
168
- }
169
- return this._prop('hidden');
120
+ return this._prop('hidden', false);
170
121
  },
171
122
 
172
123
  // Return visibility state: 'visible', 'hidden' or 'prerender'.
@@ -182,10 +133,7 @@
182
133
  // It is just proxy to `document.visibilityState`, but use
183
134
  // vendor prefix.
184
135
  state: function () {
185
- if ( !this.isSupported() ) {
186
- return 'visible';
187
- }
188
- return this._prop('visibilityState');
136
+ return this._prop('visibilityState', 'visible');
189
137
  },
190
138
 
191
139
  // Return true if browser support Page Visibility API.
@@ -213,22 +161,11 @@
213
161
  // by `_setListener` method.
214
162
  _listening: false,
215
163
 
216
- // Callbacks from `change` method, that wait visibility changes.
217
- _changeCallbacks: [],
218
-
219
- // Callbacks from `onVisible` method, that wait when page become to be
220
- // visible.
221
- _onVisibleCallbacks: [],
222
-
223
- // Callbacks from `afterPrerendering` method, that wait when visibility
224
- // state change from “prerender”.
225
- _afterPrerenderingCallbacks: [],
226
-
227
164
  // Last timer number.
228
- _lastTimer: 0,
165
+ _lastCallback: -1,
229
166
 
230
- // Callbacks and intervals added by `every` method.
231
- _timers: { },
167
+ // Callbacks from `change` method, that wait visibility changes.
168
+ _callbacks: { },
232
169
 
233
170
  // Variable to check hidden-visible state changes.
234
171
  _hiddenBefore: false,
@@ -236,12 +173,6 @@
236
173
  // Initialize variables on page loading.
237
174
  _init: function () {
238
175
  this._hiddenBefore = this.hidden();
239
-
240
- if ( defined(window.jQuery) && defined(jQuery.every) ) {
241
- this._setInterval = this._chronoSetInterval;
242
- } else {
243
- this._setInterval = this._originalSetInterval;
244
- }
245
176
  },
246
177
 
247
178
  // Detect vendor prefix and return it.
@@ -273,43 +204,23 @@
273
204
  },
274
205
 
275
206
  // Return document’s property value with name with vendor prefix.
276
- _prop: function (name) {
277
- return this._doc[this._name(name)]
207
+ // If API is not support, it will retun `unsupported` value.
208
+ _prop: function (name, unsupported) {
209
+ if ( !this.isSupported() ) {
210
+ return unsupported;
211
+ }
212
+ return this._doc[this._name(name)];
278
213
  },
279
214
 
280
215
  // Listener for `visibilitychange` event.
281
- _onVisibilityChange: function(event) {
282
- var isHidden = this.hidden(),
283
- state = this.state();
284
-
285
- for ( var i = 0; i < this._changeCallbacks.length; i++ ) {
286
- this._changeCallbacks[i].call(this._doc, event, state);
287
- }
288
-
289
- var hiddenBefore = this._hiddenBefore;
290
- if ( (isHidden && !hiddenBefore) || (!isHidden && hiddenBefore) ) {
291
- for ( i in this._timers ) {
292
- this._stopTimer(i);
293
- this._runTimer(i, !isHidden);
294
- }
295
- }
296
-
297
- if ( !isHidden ) {
298
- for ( var i = 0; i < this._onVisibleCallbacks.length; i++ ) {
299
- this._onVisibleCallbacks[i]();
300
- }
301
- this._onVisibleCallbacks = [];
302
- }
216
+ _onChange: function(event) {
217
+ var state = this.state();
303
218
 
304
- if ( 'prerender' != this.state() ) {
305
- var after = this._afterPrerenderingCallbacks.length
306
- for ( var i = 0; i < after; i++ ) {
307
- this._afterPrerenderingCallbacks[i]();
308
- }
309
- this._afterPrerenderingCallbacks = [];
219
+ for ( var i in this._callbacks ) {
220
+ this._callbacks[i].call(this._doc, event, state);
310
221
  }
311
222
 
312
- this._hiddenBefore = isHidden;
223
+ this._hiddenBefore = this.hidden();
313
224
  },
314
225
 
315
226
  // Set listener for `visibilitychange` event.
@@ -319,7 +230,7 @@
319
230
  }
320
231
  var event = this._prefix() + 'visibilitychange';
321
232
  var listener = function () {
322
- Visibility._onVisibilityChange.apply(Visibility, arguments);
233
+ Visibility._onChange.apply(Visibility, arguments);
323
234
  };
324
235
  if ( this._doc.addEventListener ) {
325
236
  this._doc.addEventListener(event, listener, false);
@@ -328,63 +239,184 @@
328
239
  }
329
240
  this._listening = true;
330
241
  this._hiddenBefore = this.hidden();
331
- },
242
+ }
332
243
 
333
- // Set interval directly by `setInterval` function without any syntax
334
- // sugar.
335
- _originalSetInterval: function (callback, interval) {
336
- return setInterval(callback, interval);
337
- },
244
+ };
338
245
 
339
- // Set interval by jQuery Chrono plugin. Add syntax sugar to `interval`
340
- // and `hiddenInterval` arguments, such as "1 second" and others.
341
- //
342
- // It will be automatically set to `_setInterval` on loading if
343
- // you include jQuery Chrono plugin before Visibility.js.
344
- _chronoSetInterval: function (callback, internal) {
345
- return jQuery.every(internal, callback);
346
- },
246
+ Visibility._init();
347
247
 
348
- // Set interval by `setInterval`. Allow to change function for tests or
349
- // syntax sugar in `interval` arguments.
350
- //
351
- // Function will be automatically set in `_init` method (which will be
352
- // call on script loading). So you must include jQuery Chrono plugin
353
- // before Visibility.js.
354
- _setInterval: null,
355
-
356
- // Try to run timer from every method by it’s ID. It will be use
357
- // `interval` or `hiddenInterval` depending on visibility state.
358
- // If page is hidden and `hiddenInterval` is null,
359
- // it will not run timer.
360
- //
361
- // Argument `now` say, that timers must be execute now too.
362
- _runTimer: function (id, now) {
363
- var interval,
364
- timer = this._timers[id];
365
- if ( this.hidden() ) {
366
- if ( null === timer.hiddenInterval ) {
367
- return;
368
- }
369
- interval = timer.hiddenInterval;
370
- } else {
371
- interval = timer.interval;
372
- }
373
- if ( now ) {
374
- timer.callback.call(window);
375
- }
376
- timer.intervalID = this._setInterval(timer.callback, interval);
377
- },
378
248
 
379
- // Stop timer from `every` method by it’s ID.
380
- _stopTimer: function (id) {
381
- var timer = this._timers[id];
382
- clearInterval(timer.intervalID);
383
- delete timer.intervalID;
384
- }
249
+ var timers = {
250
+
251
+ // Run callback every `interval` milliseconds if page is visible and
252
+ // every `hiddenInterval` milliseconds if page is hidden.
253
+ //
254
+ // Visibility.every(60 * 1000, 5 * 60 * 1000, function () {
255
+ // checkNewMails();
256
+ // });
257
+ //
258
+ // You can skip `hiddenInterval` and callback will be called only if
259
+ // page is visible.
260
+ //
261
+ // Visibility.every(1000, function () {
262
+ // updateCountdown();
263
+ // });
264
+ //
265
+ // It is analog of `setInterval(callback, interval)` but use visibility
266
+ // state.
267
+ //
268
+ // It return timer ID, that you can use in `Visibility.stop(id)` to stop
269
+ // timer (`clearInterval` analog).
270
+ // Warning: timer ID is different from interval ID from `setInterval`,
271
+ // so don’t use it in `clearInterval`.
272
+ //
273
+ // On change state from hidden to visible timers will be execute.
274
+ //
275
+ // If you include jQuery Chrono plugin before Visibility.js, you could
276
+ // use Chrono’s syntax sugar in interval arguments:
277
+ //
278
+ // Visibility.every('second', function () {
279
+ // updateCountdown();
280
+ // });
281
+ // Visibility.every('1 minute', '5 minutes', function () {
282
+ // checkNewMails();
283
+ // });
284
+ every: function (interval, hiddenInterval, callback) {
285
+ this._initTimers();
286
+
287
+ if ( !defined(callback) ) {
288
+ callback = hiddenInterval;
289
+ hiddenInterval = null;
290
+ }
291
+ this._lastTimer += 1;
292
+ var number = this._lastTimer;
293
+ this._timers[number] = ({
294
+ interval: interval,
295
+ hiddenInterval: hiddenInterval,
296
+ callback: callback
297
+ });
298
+ this._runTimer(number, false);
299
+
300
+ if ( this.isSupported() ) {
301
+ this._setListener();
302
+ }
303
+ return number;
304
+ },
305
+
306
+ // Stop timer from `every` method by it ID (`every` method return it).
307
+ //
308
+ // slideshow = Visibility.every(5 * 1000, function () {
309
+ // changeSlide();
310
+ // });
311
+ // $('.stopSlideshow').click(function () {
312
+ // Visibility.stop(slideshow);
313
+ // });
314
+ stop: function(id) {
315
+ var timer = this._timers[id]
316
+ if ( !defined(timer) ) {
317
+ return false;
318
+ }
319
+ this._stopTimer(id);
320
+ delete this._timers[id];
321
+ return timer;
322
+ },
323
+
324
+ // Last timer number.
325
+ _lastTimer: -1,
326
+
327
+ // Callbacks and intervals added by `every` method.
328
+ _timers: { },
329
+
330
+ // Is setInterval method detected and listener is binded.
331
+ _timersInitialized: false,
332
+
333
+ // Initialize variables on page loading.
334
+ _initTimers: function () {
335
+ if ( this._timersInitialized ) {
336
+ return;
337
+ }
338
+ this._timersInitialized = true;
339
+
340
+ if ( defined(window.jQuery) && defined(jQuery.every) ) {
341
+ this._setInterval = this._chronoInterval;
342
+ } else {
343
+ this._setInterval = this._originalInterval;
344
+ }
345
+ this.change(function () {
346
+ Visibility._timersStopRun()
347
+ });
348
+ },
349
+
350
+ // Set interval directly by `setInterval` function without any syntax
351
+ // sugar.
352
+ _originalInterval: function (callback, interval) {
353
+ return setInterval(callback, interval);
354
+ },
355
+
356
+ // Set interval by jQuery Chrono plugin. Add syntax sugar to `interval`
357
+ // and `hiddenInterval` arguments, such as "1 second" and others.
358
+ //
359
+ // It will be automatically set to `_setInterval` on loading if
360
+ // you include jQuery Chrono plugin before Visibility.js.
361
+ _chronoInterval: function (callback, internal) {
362
+ return jQuery.every(internal, callback);
363
+ },
364
+
365
+ // Set interval by `setInterval`. Allow to change function for tests or
366
+ // syntax sugar in `interval` arguments.
367
+ //
368
+ // Function will be automatically set in `_init` method (which will be
369
+ // call on script loading). So you must include jQuery Chrono plugin
370
+ // before Visibility.js.
371
+ _setInterval: null,
372
+
373
+ // Try to run timer from every method by it’s ID. It will be use
374
+ // `interval` or `hiddenInterval` depending on visibility state.
375
+ // If page is hidden and `hiddenInterval` is null,
376
+ // it will not run timer.
377
+ //
378
+ // Argument `now` say, that timers must be execute now too.
379
+ _runTimer: function (id, now) {
380
+ var interval,
381
+ timer = this._timers[id];
382
+ if ( this.hidden() ) {
383
+ if ( null === timer.hiddenInterval ) {
384
+ return;
385
+ }
386
+ interval = timer.hiddenInterval;
387
+ } else {
388
+ interval = timer.interval;
389
+ }
390
+ if ( now ) {
391
+ timer.callback.call(window);
392
+ }
393
+ timer.id = this._setInterval(timer.callback, interval);
394
+ },
395
+
396
+ // Stop timer from `every` method by it’s ID.
397
+ _stopTimer: function (id) {
398
+ var timer = this._timers[id];
399
+ clearInterval(timer.id);
400
+ delete timer.id;
401
+ },
402
+
403
+ // Listener for `visibilitychange` event.
404
+ _timersStopRun: function (event) {
405
+ var isHidden = this.hidden(),
406
+ hiddenBefore = this._hiddenBefore;
407
+
408
+ if ( (isHidden && !hiddenBefore) || (!isHidden && hiddenBefore) ) {
409
+ for ( var i in this._timers ) {
410
+ this._stopTimer(i);
411
+ this._runTimer(i, !isHidden);
412
+ }
413
+ }
414
+ }
385
415
 
386
416
  };
387
417
 
388
- Visibility._init();
418
+ for ( var prop in timers ) {
419
+ Visibility[prop] = timers[prop];
420
+ }
389
421
 
390
422
  })();
@@ -0,0 +1,199 @@
1
+ /*
2
+ * Copyright 2012 Andrey “A.I.” Sitnik <andrey@sitnik.ru>,
3
+ * sponsored by Evil Martians.
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU Lesser General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU Lesser General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Lesser General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ ;(function () {
20
+ "use strict";
21
+
22
+ var defined = function(variable) {
23
+ return ('undefined' != typeof(variable));
24
+ };
25
+
26
+ var timers = {
27
+
28
+ // Run callback every `interval` milliseconds if page is visible and
29
+ // every `hiddenInterval` milliseconds if page is hidden.
30
+ //
31
+ // Visibility.every(60 * 1000, 5 * 60 * 1000, function () {
32
+ // checkNewMails();
33
+ // });
34
+ //
35
+ // You can skip `hiddenInterval` and callback will be called only if
36
+ // page is visible.
37
+ //
38
+ // Visibility.every(1000, function () {
39
+ // updateCountdown();
40
+ // });
41
+ //
42
+ // It is analog of `setInterval(callback, interval)` but use visibility
43
+ // state.
44
+ //
45
+ // It return timer ID, that you can use in `Visibility.stop(id)` to stop
46
+ // timer (`clearInterval` analog).
47
+ // Warning: timer ID is different from interval ID from `setInterval`,
48
+ // so don’t use it in `clearInterval`.
49
+ //
50
+ // On change state from hidden to visible timers will be execute.
51
+ //
52
+ // If you include jQuery Chrono plugin before Visibility.js, you could
53
+ // use Chrono’s syntax sugar in interval arguments:
54
+ //
55
+ // Visibility.every('second', function () {
56
+ // updateCountdown();
57
+ // });
58
+ // Visibility.every('1 minute', '5 minutes', function () {
59
+ // checkNewMails();
60
+ // });
61
+ every: function (interval, hiddenInterval, callback) {
62
+ this._initTimers();
63
+
64
+ if ( !defined(callback) ) {
65
+ callback = hiddenInterval;
66
+ hiddenInterval = null;
67
+ }
68
+ this._lastTimer += 1;
69
+ var number = this._lastTimer;
70
+ this._timers[number] = ({
71
+ interval: interval,
72
+ hiddenInterval: hiddenInterval,
73
+ callback: callback
74
+ });
75
+ this._runTimer(number, false);
76
+
77
+ if ( this.isSupported() ) {
78
+ this._setListener();
79
+ }
80
+ return number;
81
+ },
82
+
83
+ // Stop timer from `every` method by it ID (`every` method return it).
84
+ //
85
+ // slideshow = Visibility.every(5 * 1000, function () {
86
+ // changeSlide();
87
+ // });
88
+ // $('.stopSlideshow').click(function () {
89
+ // Visibility.stop(slideshow);
90
+ // });
91
+ stop: function(id) {
92
+ var timer = this._timers[id]
93
+ if ( !defined(timer) ) {
94
+ return false;
95
+ }
96
+ this._stopTimer(id);
97
+ delete this._timers[id];
98
+ return timer;
99
+ },
100
+
101
+ // Last timer number.
102
+ _lastTimer: -1,
103
+
104
+ // Callbacks and intervals added by `every` method.
105
+ _timers: { },
106
+
107
+ // Is setInterval method detected and listener is binded.
108
+ _timersInitialized: false,
109
+
110
+ // Initialize variables on page loading.
111
+ _initTimers: function () {
112
+ if ( this._timersInitialized ) {
113
+ return;
114
+ }
115
+ this._timersInitialized = true;
116
+
117
+ if ( defined(window.jQuery) && defined(jQuery.every) ) {
118
+ this._setInterval = this._chronoInterval;
119
+ } else {
120
+ this._setInterval = this._originalInterval;
121
+ }
122
+ this.change(function () {
123
+ Visibility._timersStopRun()
124
+ });
125
+ },
126
+
127
+ // Set interval directly by `setInterval` function without any syntax
128
+ // sugar.
129
+ _originalInterval: function (callback, interval) {
130
+ return setInterval(callback, interval);
131
+ },
132
+
133
+ // Set interval by jQuery Chrono plugin. Add syntax sugar to `interval`
134
+ // and `hiddenInterval` arguments, such as "1 second" and others.
135
+ //
136
+ // It will be automatically set to `_setInterval` on loading if
137
+ // you include jQuery Chrono plugin before Visibility.js.
138
+ _chronoInterval: function (callback, internal) {
139
+ return jQuery.every(internal, callback);
140
+ },
141
+
142
+ // Set interval by `setInterval`. Allow to change function for tests or
143
+ // syntax sugar in `interval` arguments.
144
+ //
145
+ // Function will be automatically set in `_init` method (which will be
146
+ // call on script loading). So you must include jQuery Chrono plugin
147
+ // before Visibility.js.
148
+ _setInterval: null,
149
+
150
+ // Try to run timer from every method by it’s ID. It will be use
151
+ // `interval` or `hiddenInterval` depending on visibility state.
152
+ // If page is hidden and `hiddenInterval` is null,
153
+ // it will not run timer.
154
+ //
155
+ // Argument `now` say, that timers must be execute now too.
156
+ _runTimer: function (id, now) {
157
+ var interval,
158
+ timer = this._timers[id];
159
+ if ( this.hidden() ) {
160
+ if ( null === timer.hiddenInterval ) {
161
+ return;
162
+ }
163
+ interval = timer.hiddenInterval;
164
+ } else {
165
+ interval = timer.interval;
166
+ }
167
+ if ( now ) {
168
+ timer.callback.call(window);
169
+ }
170
+ timer.id = this._setInterval(timer.callback, interval);
171
+ },
172
+
173
+ // Stop timer from `every` method by it’s ID.
174
+ _stopTimer: function (id) {
175
+ var timer = this._timers[id];
176
+ clearInterval(timer.id);
177
+ delete timer.id;
178
+ },
179
+
180
+ // Listener for `visibilitychange` event.
181
+ _timersStopRun: function (event) {
182
+ var isHidden = this.hidden(),
183
+ hiddenBefore = this._hiddenBefore;
184
+
185
+ if ( (isHidden && !hiddenBefore) || (!isHidden && hiddenBefore) ) {
186
+ for ( var i in this._timers ) {
187
+ this._stopTimer(i);
188
+ this._runTimer(i, !isHidden);
189
+ }
190
+ }
191
+ }
192
+
193
+ };
194
+
195
+ for ( var prop in timers ) {
196
+ Visibility[prop] = timers[prop];
197
+ }
198
+
199
+ })();
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: visibilityjs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.5
4
+ version: '0.5'
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-22 00:00:00.000000000 Z
12
+ date: 2012-07-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sprockets
@@ -41,6 +41,8 @@ extra_rdoc_files:
41
41
  - ChangeLog
42
42
  files:
43
43
  - lib/assets/javascripts/visibility.js
44
+ - lib/assets/javascripts/visibility.core.js
45
+ - lib/assets/javascripts/visibility.timers.js
44
46
  - lib/assets/javascripts/visibility.fallback.js
45
47
  - lib/visibilityjs.rb
46
48
  - LICENSE