Scoping issue for nested class - javascript

I cannot manage to access the method of the nested class. This is what I have tried so far. The main issue is calling TimerTask.execute(). The error states that the task is undefined.
Uncaught TypeError: undefined is not a function
The program should display an increment number for ten runs on a timer.
var TimerTask = new Class({
initalize: function (options) {
this.counter = options.counter;
this.run = options.run;
this.onComplete = options.complete;
this.done = false;
},
execute : function() {
var me = this;
this.counter--;
if (this.done === false || this.counter <= 0) {
this.done = true;
me.onComplete.call(me);
} else {
me.run.call(me);
}
}
});
var Timer = new Class({
initialize: function (options) {
this.id = 0;
this.running = true;
this.count = 0;
this.delay = options.delay || 1000;
this.tasks = options.tasks || [];
},
start: function () {
var me = this;
me.id = setInterval(function tick() {
if (!me.running) return;
for (var i = 0; i < me.tasks.length; i++) {
me.tasks[i].execute();
}
me.count++;
}, this.delay);
},
pause: function pause() {
this.running = false;
return this;
},
run: function run() {
this.running = true;
return this;
},
stop: function stop() {
clearInterval(this.id);
this.stopped = true;
return this;
},
schedule: function (task) {
this.tasks.push(task);
}
});
var i = 0;
var t1 = new Timer({
delay: 1000,
tasks: [
new TimerTask({
run: function () {
document.getElementById('output').innerHTML = parseInt(i++, 10);
},
onComplete: function () {
alert('DONE!');
},
counter: 10
})]
});
t1.start();
<script src="//cdnjs.cloudflare.com/ajax/libs/mootools/1.5.0/mootools-core-full-compat.min.js"></script>
<div id="output"></div>

Oh, wow. Cannot believe that no one caught the typo in TimerTask. No wonder I kept getting the prototype instead of the instance. I spelled "initialize" without the 3rd "i"...
Anyways, I fixed the issue and the code is glorious.
Note: I was using this as the basis for my timer -- https://gist.github.com/NV/363465
var TimerTask = new Class({
initialize: function (options) {
this.counter = options.counter || 0;
this.run = options.run;
this.onComplete = options.onComplete;
this.active = true;
this.isInfinite = this.counter === 0;
},
execute: function () {
if (!this.isInfinite && this.counter === 0) {
this.active = false;
if (this.onComplete !== undefined) {
this.onComplete();
}
} else {
this.run();
if (!this.isInfinite) {
this.counter--;
}
}
},
isActive: function () {
return this.active;
}
});
var Timer = new Class({
initialize: function (options) {
this.id = 0;
this.running = true;
this.count = 0;
this.delay = options.delay || 1000;
this.tasks = options.tasks || [];
Timer.all.push(this);
},
start: function () {
var me = this;
me.id = setInterval(function tick() {
if (!me.running) return;
for (var i = 0; i < me.tasks.length; i++) {
var task = me.tasks[i];
if (task.isActive()) {
task.execute();
} else {
console.log('Task is complete...');
}
}
me.count++;
}, this.delay);
},
pause: function pause() {
this.running = false;
return this;
},
run: function run() {
this.running = true;
return this;
},
stop: function stop() {
clearInterval(this.id);
this.stopped = true;
return this;
},
schedule: function (task) {
this.tasks.push(task);
}
});
Timer.extend({
all : [],
pause : function pause() {
var all = Timer.all;
for (var i = 0; i < all.length; i++) {
all[i].pause();
}
return all;
},
run : function run() {
var all = Timer.all;
for (var i = 0; i < all.length; i++) {
all[i].run();
}
return all;
},
stop : function stop() {
var all = Timer.all;
for (var i = 0; i < all.length; i++) {
all[i].stop();
}
return all;
}
});
function print(id, value) {
document.getElementById(id).innerHTML = value;
}
var i = 0;
var t1 = new Timer({
delay: 100,
tasks: [
new TimerTask({
run: function () {
print('output1', String.fromCharCode(65 + i));
},
onComplete: function () {
console.log('Task 1 complete...');
},
counter: 26
}),
new TimerTask({
run: function () {
print('output2', parseInt(i++, 10));
}
})]
});
t1.start();
// After 4 seconds, stop all timers.
setTimeout(function() {
Timer.stop();
}, 4000);
<script src="//cdnjs.cloudflare.com/ajax/libs/mootools/1.5.0/mootools-core-full-compat.min.js"></script>
<div id="output1"></div>
<div id="output2"></div>

Related

Issues with parallax/translate3d performance on safari & firefox?

For days I've been trying to figure out how get a nice, smooth, hardware accelerated parallax effect working.
I'm using this repository: https://github.com/GianlucaGuarini/parallax
I've tried throttling with underscores, using css3 transitions to smooth things out, nuking the image quality, but no luck, still jank. It's very smooth in Chrome though. Other repositories I've found either have performance issues, don't work on iOS, or require jQuery.
Any similar experiences or tips with debugging?
The Squarespace team has done an awesome job with their Marquee theme. Not sure how they got it so performant.
Here is a link to the code:
https://jsfiddle.net/oh3xwgk1/3/
Edit:
One last note. I've done testing with safari and chrome dev tools. Both show durations that are a fraction of a millisecond, so I don't think its related to that?
Edit2:
By "jank" I mean jitter or a drop of frames.
HTML
<section class="relative height overflow-hidden fill-black">
<img class="parallax" src="https://placeimg.com/1000/1000/nature" alt="">
</section>
JS
(function (global, factory) {
if (typeof define === "function" && define.amd) {
define('Parallax', ['module'], factory);
} else if (typeof exports !== "undefined") {
factory(module);
} else {
var mod = {
exports: {}
};
factory(mod);
global.Parallax = mod.exports;
}
})(this, function (module) {
'use strict';
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var _createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
function $$(selector, ctx) {
var els;
if (typeof selector == 'string') els = (ctx || document).querySelectorAll(selector);else els = selector;
return Array.prototype.slice.call(els);
}
function extend(src) {
var obj,
args = arguments;
for (var i = 1; i < args.length; ++i) {
if (obj = args[i]) {
for (var key in obj) {
src[key] = obj[key];
}
}
}
return src;
}
function isUndefined(val) {
return typeof val == 'undefined';
}
function elementData(el, attr) {
if (attr) return el.dataset[attr] || el.getAttribute('data-' + attr);else return el.dataset || Array.prototype.slice.call(el.attributes).reduce(function (ret, attribute) {
if (/data-/.test(attribute.name)) ret[attribute.name] = attribute.value;
return ret;
}, {});
}
function prefix(obj, prop, value) {
var prefixes = ['ms', 'o', 'Moz', 'webkit', ''],
i = prefixes.length;
while (i--) {
var prefix = prefixes[i],
p = prefix ? prefix + prop[0].toUpperCase() + prop.substr(1) : prop.toLowerCase() + prop.substr(1);
if (p in obj) {
obj[p] = value;
return true;
}
}
return false;
}
var observable = function observable(el) {
el = el || {};
var callbacks = {},
slice = Array.prototype.slice,
onEachEvent = function onEachEvent(e, fn) {
e.replace(/\S+/g, fn);
},
defineProperty = function defineProperty(key, value) {
Object.defineProperty(el, key, {
value: value,
enumerable: false,
writable: false,
configurable: false
});
};
defineProperty('on', function (events, fn) {
if (typeof fn != 'function') return el;
onEachEvent(events, function (name, pos) {
(callbacks[name] = callbacks[name] || []).push(fn);
fn.typed = pos > 0;
});
return el;
});
defineProperty('off', function (events, fn) {
if (events == '*' && !fn) callbacks = {};else {
onEachEvent(events, function (name) {
if (fn) {
var arr = callbacks[name];
for (var i = 0, cb; cb = arr && arr[i]; ++i) {
if (cb == fn) arr.splice(i--, 1);
}
} else delete callbacks[name];
});
}
return el;
});
defineProperty('one', function (events, fn) {
function on() {
el.off(events, on);
fn.apply(el, arguments);
}
return el.on(events, on);
});
defineProperty('trigger', function (events) {
var args = slice.call(arguments, 1),
fns;
onEachEvent(events, function (name) {
fns = slice.call(callbacks[name] || [], 0);
for (var i = 0, fn; fn = fns[i]; ++i) {
if (fn.busy) return;
fn.busy = 1;
fn.apply(el, fn.typed ? [name].concat(args) : args);
if (fns[i] !== fn) {
i--;
}
fn.busy = 0;
}
if (callbacks['*'] && name != '*') el.trigger.apply(el, ['*', name].concat(args));
});
return el;
});
return el;
};
var rAF = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame || window.oRequestAnimationFrame || function (cb) {
setTimeout(cb, 1000 / 60);
};
var RESIZE_DELAY = 20;
var Stage = function () {
function Stage() {
_classCallCheck(this, Stage);
observable(this);
this.resizeTimer = null;
this.tick = false;
this.bind();
}
_createClass(Stage, [{
key: 'bind',
value: function bind() {
var _this = this;
window.addEventListener('scroll', function () {
return _this.scroll();
}, true);
window.addEventListener('mousewheel', function () {
return _this.scroll();
}, true);
window.addEventListener('touchmove', function () {
return _this.scroll();
}, true);
window.addEventListener('resize', function () {
return _this.resize();
}, true);
window.addEventListener('orientationchange', function () {
return _this.resize();
}, true);
window.onload = function () {
return _this.scroll();
};
return this;
}
}, {
key: 'scroll',
value: function scroll() {
var _this2 = this;
if (this.tick) return this;
this.tick = !this.tick;
rAF(function () {
return _this2.update();
});
return this;
}
}, {
key: 'update',
value: function update() {
this.trigger('scroll', this.scrollTop);
this.tick = !this.tick;
return this;
}
}, {
key: 'resize',
value: function resize() {
var _this3 = this;
if (this.resizeTimer) clearTimeout(this.resizeTimer);
this.resizeTimer = setTimeout(function () {
return _this3.trigger('resize', _this3.size);
}, RESIZE_DELAY);
return this;
}
}, {
key: 'scrollTop',
get: function get() {
var top = (window.pageYOffset || document.scrollTop) - (document.clientTop || 0);
return window.isNaN(top) ? 0 : top;
}
}, {
key: 'height',
get: function get() {
return window.innerHeight;
}
}, {
key: 'width',
get: function get() {
return window.innerWidth;
}
}, {
key: 'size',
get: function get() {
return {
width: this.width,
height: this.height
};
}
}]);
return Stage;
}();
var HAS_TRANSLATE_3D = function (div) {
prefix(div.style, 'transform', 'translate3d(0, 0, 0)');
return (/translate3d/g.test(div.style.cssText)
);
}(document.createElement('div'));
var Canvas = function () {
function Canvas(img, opts) {
_classCallCheck(this, Canvas);
observable(this);
this.opts = opts;
this.img = img;
this.wrapper = img.parentNode;
this.isLoaded = false;
}
_createClass(Canvas, [{
key: 'load',
value: function load() {
var _this4 = this;
if (!this.img.width || !this.img.height || !this.img.complete) this.img.onload = function () {
return _this4.onImageLoaded();
};else this.onImageLoaded();
return this;
}
}, {
key: 'onImageLoaded',
value: function onImageLoaded() {
this.isLoaded = true;
this.update();
this.trigger('loaded', this.img);
return this;
}
}, {
key: 'update',
value: function update() {
var iw = this.img.naturalWidth || this.img.width,
ih = this.img.naturalHeight || this.img.height,
ratio = iw / ih,
size = this.size;
if (size.width / ratio <= size.height) {
this.img.height = size.height;
this.img.width = size.height * ratio;
} else {
this.img.width = size.width;
this.img.height = size.width / ratio;
}
this.img.style.top = - ~ ~((this.img.height - size.height) / 2) + 'px';
this.img.style.left = - ~ ~((this.img.width - size.width) / 2) + 'px';
return this;
}
}, {
key: 'draw',
value: function draw(stage) {
var size = this.size,
perc = (this.offset.top + size.height * this.opts.center + stage.height / 2 - stage.scrollTop) / stage.height - 1;
perc *= this.img.height / size.height / 2 * this.opts.intensity;
if (HAS_TRANSLATE_3D) prefix(this.img.style, 'transform', 'translate3d(0, ' + -perc.toFixed(4) + '%, 0)');else prefix(this.img.style, 'transform', 'translate(0, ' + -perc + '%, 0)');
return this;
}
}, {
key: 'bounds',
get: function get() {
return this.wrapper.getBoundingClientRect();
}
}, {
key: 'offset',
get: function get() {
return {
top: this.wrapper.offsetTop,
left: this.wrapper.offsetLeft
};
}
}, {
key: 'size',
get: function get() {
var props = this.bounds;
return {
height: props.height | 0,
width: props.width | 0
};
}
}]);
return Canvas;
}();
var stage;
var Parallax = function () {
function Parallax(selector) {
var opts = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
_classCallCheck(this, Parallax);
observable(this);
this.opts = opts;
this.selector = selector;
this.canvases = [];
this.add(selector);
if (!stage) stage = new Stage();
return this;
}
_createClass(Parallax, [{
key: 'init',
value: function init() {
if (!this.canvases.length) {
console.warn('No images were found with the selector "' + this.selector + '"');
} else {
this.imagesLoaded = 0;
this.bind();
}
return this;
}
}, {
key: 'bind',
value: function bind() {
var _this5 = this;
this._onResize = function () {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return _this5.resize.apply(_this5, args);
};
this._onScroll = function () {
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
return _this5.scroll.apply(_this5, args);
};
stage.on('resize', this._onResize);
stage.on('scroll', this._onScroll);
this.canvases.forEach(function (canvas) {
canvas.one('loaded', function () {
return _this5.onCanvasLoaded(canvas);
});
canvas.load();
});
return this;
}
}, {
key: 'refresh',
value: function refresh() {
this.onResize(stage.size).onScroll(stage.scrollTop);
return this;
}
}, {
key: 'onCanvasLoaded',
value: function onCanvasLoaded(canvas) {
this.trigger('image:loaded', canvas.img, canvas);
this.imagesLoaded++;
canvas.draw(stage);
if (this.imagesLoaded == this.canvases.length) this.trigger('images:loaded');
return this;
}
}, {
key: 'scroll',
value: function scroll(scrollTop) {
var i = this.canvases.length,
offsetYBounds = this.opts.offsetYBounds,
stageScrollTop = stage.scrollTop;
while (i--) {
var canvas = this.canvases[i],
canvasHeight = canvas.size.height,
canvasOffset = canvas.offset,
canvasScrollDelta = canvasOffset.top + canvasHeight - stageScrollTop;
if (canvas.isLoaded && canvasScrollDelta + offsetYBounds > 0 && canvasScrollDelta - offsetYBounds < stageScrollTop + stage.height) {
canvas.draw(stage);
this.trigger('draw', canvas.img);
}
}
this.trigger('update', stageScrollTop);
return this;
}
}, {
key: 'add',
value: function add(els) {
this.canvases = this.canvases.concat(this.createCanvases($$(els)));
return this;
}
}, {
key: 'remove',
value: function remove(els) {
var _this6 = this;
$$(els).forEach(function (el) {
var i = _this6.canvases.length;
while (i--) {
if (el == _this6.canvases[i].img) {
_this6.canvases.splice(i, 1);
break;
}
}
});
return this;
}
}, {
key: 'destroy',
value: function destroy() {
this.off('*');
this.canvases = [];
stage.off('resize', this._onResize).off('scroll', this._onScroll);
return this;
}
}, {
key: 'resize',
value: function resize(size) {
var i = this.canvases.length;
while (i--) {
var canvas = this.canvases[i];
if (!canvas.isLoaded) return;
canvas.update().draw(stage);
}
this.trigger('resize');
return this;
}
}, {
key: 'createCanvases',
value: function createCanvases(els) {
var _this7 = this;
return els.map(function (el) {
var data = elementData(el);
return new Canvas(el, {
intensity: !isUndefined(data.intensity) ? +data.intensity : _this7.opts.intensity,
center: !isUndefined(data.center) ? +data.center : _this7.opts.center
});
});
}
}, {
key: 'opts',
set: function set(opts) {
this._defaults = {
offsetYBounds: 50,
intensity: 30,
center: 0.5
};
extend(this._defaults, opts);
},
get: function get() {
return this._defaults;
}
}]);
return Parallax;
}();
module.exports = Parallax;
});
var parallax = new Parallax('.parallax', {
offsetYBounds: 50,
intensity: 50,
center: .75
}).init();
A good solution I found for this was to create a fixed position div and placing it behind the main content. Parallax performance is tricky for big images when you're using parallax so your best case scenario is to use CSS's positioning and javascript to cleverly hide & show. At least in my opinion.

Why is only the last object's function getting called here?

Wow. I finally figured about what is causing the bug, but I can't figure out why. I have an object with a property (excuse the massive code dump)
// relatives second indices in the video to events
// that are called when the video reaches that second
this.PausePoints = [
{
sec: 10,
name: "Point number 1",
passed: false,
func: (function(that) {
this.$layer = that.GetLayerElement(10);
this.$layer.hide();
this.to = function () {
that.videlem.pause(); // pause video
$(window).resize(); // re-proportion stuff
// point the 3 mouse pointers
var $mptrs = this.$layer.find('.filmstrip-pointer');
for (var i = 0; i < $mptrs.length; ++i) {
(function (j) {
setTimeout(function () {
Point($mptrs.eq(j));
}, j * 1000);
})(i);
}
};
// attach click event to 3 sections
$clickRegions = $layer.find('div.click-region');
$clickRegions.click(function(){
$clickRegions.removeClass('clicked');
$(this).addClass('clicked');
});
this.away = function () {
this.$layer.hide();
}
// attach event to next button
$layer.find('.next-btn').click(function(){
this.away();
that.videlem.play();
}.bind(this));
return this;
})(this)
},
{
sec: 26,
name: "Point number 2",
passed: false,
func: (function(that) {
this.$layer = that.GetLayerElement(26);
this.$layer.hide();
this.to = function () {
// loop video between 0:26-0:31
this.loop = setInterval(function () {
that.videlem.currentTime = 26;
that.videlem.play();
}, 5000);
// point the 3 mouse pointers
var $mptrs = this.$layer.find('.filmstrip-pointer');
for (var i = 0; i < $mptrs.length; ++i) {
(function (j) {
setTimeout(function () {
Point($mptrs.eq(j));
}, j * 1000);
})(i);
}
this.$layer.show();
}
// separate pargraph words by spans
this.$layer.find('p').each(function () {
var spanned = $(this).text().split(" ").map(function (w) { return '<span class="word">' + w + '</span>'; }).join(" ");
$(this).html(spanned);
});
// add event click event on headlines
var timeouts = [];
this.$layer.find('h3').click(function () {
// clear any current 'showing' animations
timeouts.forEach(function(t){ clearTimeout(t); });
timeouts = [];
// unshow all words on the slide
this.$layer.find('span.word').removeClass('shown');
// show all words associated with the headline that was clicked
var $wspans = $(this).closest('.tower-layer').find('span.word');
for ( var i = 0; i < $wspans.length; ++i )
{
(function(j){
timeouts.push(setTimeout(function(){
$wspans.eq(j).addClass('shown');
},j*100));
})(i);
}
}.bind(this));
this.away = function () {
clearInterval(this.loop);
this.$layer.find('span.word').removeClass('shown');
$layer.hide();
that.videlem.currentTime = 31;//go to end of loop
};
// set action of "Next" button
this.$layer.find('.next-btn').click(function () {
this.away();
that.videlem.play();
}.bind(this));
return this;
})(this)
},
{
sec: 38,
name: "Point number 3",
passed: false,
func: (function(that) {
this.$layer = that.GetLayerElement(38);
this.$layer.hide();
this.to = function ( ) {
// loop video between 0:38-0:43
this.loop = setInterval(function () {
that.videlem.currentTime = 38;
that.videlem.play();
}, 5000);
this.$layer.show();
}
this.away = function(){
clearInterval(this.loop);
this.$layer.hide();
};
this.$layer.find('.next-btn').click(function(){
that.videlem.currentTime = 43;
this.away();
that.videlem.play();
}.bind(this));
return this;
})(this)
},
{
sec: 47,
name: "Point number 4",
passed: false,
func: (function(that){
this.$layer = that.GetLayerElement(47);
this.$layer.hide();
this.to = function ()
{
// loop video between 0:47-0:52
this.loop = setInterval(function() {
that.videlem.currentTime = 47;
that.videlem.play();
}, 5000);
// show layer
this.$layer.show();
}
this.away = function () {
clearInterval(this.loop);
this.$layer.hide();
};
this.$layer.find('.next-btn').click(function () {
that.videlem.currentTime = 52;
this.away();
that.videlem.play();
}.bind(this));
return this;
})(this)
},
{
sec: 57,
name: "Point number 5",
passed: false,
func: (function(that){
this.$layer = that.GetLayerElement(57);
// hide initially
this.$layer.hide();
this.to = function ()
{
// loop video between 0:57-1:02
this.loop = setInterval(function () {
that.videlem.currentTime = 57;
that.videlem.play();
}, 5000);
this.$layer.show();
}
this.away = function(){
clearInterval(this.loop);
$layer.hide();
};
this.$layer.find('.next-btn').click(function () {
that.videlem.currentTime = 62;
this.away();
that.videlem.play();
}.bind(this));
return this;
})(this)
}
];
and what I'm noticing is that when I try to call any of the to functions it always calls the one in the last element of the array.
For example,
VidHandler.PausePoints[0].func.to()
calls
this.to = function ()
{
// loop video between 0:57-1:02
this.loop = setInterval(function () {
that.videlem.currentTime = 57;
that.videlem.play();
}, 5000);
this.$layer.show();
}
instead of the expected
this.to = function () {
that.videlem.pause(); // pause video
$(window).resize(); // re-proportion stuff
// point the 3 mouse pointers
var $mptrs = this.$layer.find('.filmstrip-pointer');
for (var i = 0; i < $mptrs.length; ++i) {
(function (j) {
setTimeout(function () {
Point($mptrs.eq(j));
}, j * 1000);
})(i);
}
};
Why is this happening and how can I fix it?
The problem is you're trying to assign something to func using an immediately invoked function expression (IIFE). Those IIFEs are executed before the object is constructed, meaning this refers to something else. Your code can basically be broken down like this:
this.to = function() {
// version for "Point number 1"
};
this.to = function() {
// version for "Point number 2"
// notice that you're overwriting the previous one
};
// repeat for all points
var self = this;
this.PausePoints = [
{
name: "Point number 1",
func: self
},
// repeat for all points
];
So what you're actually doing is assigning a to value to the same object that has the PausePoints property.

Javascript callback managment

I'm having trouble with designing a class which exposes its actions through callbacks. Yes my approach works for me but also seems too complex.
To illustrate the problem I've drawn the following picture. I hope it is useful for you to understand the class/model.
In my approach, I use some arrays holding user defined callback functions.
....
rocket.prototype.on = function(eventName, userFunction) {
this.callbacks[eventName].push(userFunction);
}
rocket.prototype.beforeLunch = function(){
userFunctions = this.callbacks['beforeLunch']
for(var i in userFunctions)
userFunctions[i](); // calling the user function
}
rocket.prototype.lunch = function() {
this.beforeLunch();
...
}
....
var myRocket = new Rocket();
myRocket.on('beforeLunch', function() {
// do some work
console.log('the newspaper guys are taking pictures of the rocket');
});
myRocket.on('beforeLunch', function() {
// do some work
console.log('some engineers are making last checks ');
});
I'm wondering what the most used approach is. I guess I could use promises or other libraries to make this implementation more understandable. In this slide using callbacks is considered evil. http://www.slideshare.net/TrevorBurnham/sane-async-patterns
So, should I use a library such as promise or continue and enhance my approach?
var Rocket = function () {
this.timer = null;
this.velocity = 200;
this.heightMoon = 5000;
this.goingToMoon = true;
this.rocketStatus = {
velocity: null,
height: 0,
status: null
};
this.listener = {
};
}
Rocket.prototype.report = function () {
for (var i in this.rocketStatus) {
console.log(this.rocketStatus[i]);
};
};
Rocket.prototype.on = function (name,cb) {
if (this.listener[name]){
this.listener[name].push(cb);
}else{
this.listener[name] = new Array(cb);
}
};
Rocket.prototype.initListener = function (name) {
if (this.listener[name]) {
for (var i = 0; i < this.listener[name].length; i++) {
this.listener[name][i]();
}
return true;
}else{
return false;
};
}
Rocket.prototype.launch = function () {
this.initListener("beforeLaunch");
this.rocketStatus.status = "Launching";
this.move();
this.initListener("afterLaunch");
}
Rocket.prototype.move = function () {
var that = this;
that.initListener("beforeMove");
if (that.goingToMoon) {
that.rocketStatus.height += that.velocity;
}else{
that.rocketStatus.height -= that.velocity;
};
that.rocketStatus.velocity = that.velocity;
if (that.velocity != 0) {
that.rocketStatus.status = "moving";
}else{
that.rocketStatus.status = "not moving";
};
if (that.velocity >= 600){
that.crash();
return;
}
if (that.rocketStatus.height == 2000 && that.goingToMoon)
that.leaveModules();
if (that.rocketStatus.height == that.heightMoon)
that.landToMoon();
if (that.rocketStatus.height == 0 && !that.goingToMoon){
that.landToEarth();
return;
}
that.report();
that.initListener("afterMove");
that.timer = setTimeout(function () {
that.move();
},1000)
}
Rocket.prototype.stop = function () {
clearTimeout(this.timer);
this.initListener("beforeStop");
this.velocity = 0;
this.rocketStatus.status = "Stopped";
console.log(this.rocketStatus.status)
this.initListener("afterStop");
return true;
}
Rocket.prototype.crash = function () {
this.initListener("beforeCrash");
this.rocketStatus.status = "Crashed!";
this.report();
this.stop();
this.initListener("afterCrash");
}
Rocket.prototype.leaveModules = function () {
this.initListener("beforeModules");
this.rocketStatus.status = "Leaving Modules";
this.initListener("afterModules");
}
Rocket.prototype.landToMoon = function () {
this.initListener("beforeLandToMoon");
this.rocketStatus.status = "Landing to Moon";
this.goingToMoon = false;
this.initListener("afterLandToMoon");
}
Rocket.prototype.landToEarth = function () {
this.initListener("beforeLandToEarth");
this.stop();
this.rocketStatus.status = "Landing to Earth";
this.initListener("afterLandToEarth");
}
Rocket.prototype.relaunch = function () {
this.initListener("beforeRelaunch");
this.timer = null;
this.velocity = 200;
this.heightMoon = 5000;
this.goingToMoon = true;
this.rocketStatus = {
velocity: 200,
height: 0,
status: "relaunch"
};
this.launch();
this.initListener("afterRelaunch");
}
init;
var rocket = new Rocket();
rocket.on("afterLaunch", function () {console.log("launch1")})
rocket.on("afterLandToMoon", function () {console.log("land1")})
rocket.on("beforeLandToEarth", function () {console.log("land2")})
rocket.on("afterMove", function () {console.log("move1")})
rocket.on("beforeLaunch", function () {console.log("launch2")})
rocket.launch();
You can add any function before or after any event.
This is my solution for this kinda problem. I am not using any special methods anything. I was just wonder is there any good practise for this like problems. I dig some promise,deferred but i just can't able to to this. Any ideas ?

how to clear all javascript Timeouts?

i have a loop function that in first 5 seconds it runs social1() and in second 5 seconds it runs social2() then loop ...
i have 2 hover functions too
i need clear all active timeouts because when i hover on images (.social1 & .social2), i can see that multiple timeouts are running
how to fix this?
function social1() {
$('.social1').fadeTo(500, 1);
$('.social2').fadeTo(500, 0.5);
timeout = setTimeout(function() {
social2();
}, 5000);
}
function social2() {
$('.social1').fadeTo(500, 0.5);
$('.social2').fadeTo(500, 1);
timeout = setTimeout(function() {
social1();
}, 5000);
}
$(document).ready(function ()
{
social1();
$('.social1').hover(
function () {
window.clearTimeout(timeout);
social1();
},
function () {
timeout = setTimeout(function() {
social2();
}, 5000);
}
);
$('.social2').hover(
function () {
window.clearTimeout(timeout);
social2();
},
function () {
timeout = setTimeout(function() {
social1();
}, 5000);
}
);
__EDIT__
To manage a collection of timeouts (and intervals), you could use following snippet.
This will allow to clear any timeouts or intervals set anywhere in code, although, you have to set this snippet before setting any timeout or interval. Basically, before processing any javascript code or external script which uses timeout/interval.
JS:
;(function () {
window.timeouts = {},
window.intervals = {},
window.osetTimeout = window.setTimeout,
window.osetInterval = window.setInterval,
window.oclearTimeout = window.clearTimeout,
window.oclearInterval = window.clearInterval,
window.setTimeout = function () {
var args = _parseArgs('timeouts', arguments),
timeout = window.osetTimeout.apply(this, args.args);
window.timeouts[args.ns].push(timeout);
return timeout;
},
window.setInterval = function () {
var args = _parseArgs('intervals', arguments),
interval = window.osetInterval.apply(this, args.args);
window.intervals[args.ns].push(interval);
return interval;
},
window.clearTimeout = function () {
_removeTimer('timeouts', arguments);
},
window.clearInterval = function () {
_removeTimer('intervals', arguments);
},
window.clearAllTimeout = function () {
_clearAllTimer('timeouts', arguments[0]);
},
window.clearAllInterval = function () {
_clearAllTimer('intervals', arguments[0]);
};
function _parseArgs(type, args) {
var ns = typeof args[0] === "function" ? "no_ns" : args[0];
if (ns !== "no_ns")[].splice.call(args, 0, 1);
if (!window[type][ns]) window[type][ns] = [];
return {
ns: ns,
args: args
};
}
function _removeTimer(type, args) {
var fnToCall = type === "timeouts" ? "oclearTimeout" : "oclearInterval",
timerId = args[0];
window[fnToCall].apply(this, args);
for (var k in window[type]) {
for (var i = 0, z = window[type][k].length; i < z; i++) {
if (window[type][k][i] === timerId) {
window[type][k].splice(i, 1);
if (!window[type][k].length) delete window[type][k];
return;
}
}
}
}
function _clearAllTimer(type, ns) {
var timersToClear = ns ? window[type][ns] : (function () {
var timers = [];
for (var k in window[type]) {
timers = timers.concat(window[type][k]);
}
return timers;
}());
for (var i = 0, z = timersToClear.length; i < z; i++) {
_removeTimer(type, [timersToClear[i]]);
}
}
}());
How to use it:
Set timeout(s)/interval(s) as usual:
var test1 = setTimeout(function(){/**/, 1000);
var test2 = setTimeout(function(){/**/, 1000);
Then you could use to clear both:
clearAllTimeout(); // clearAllInterval(); for intervals
This will clear both timeouts (test1 & test2)
You can use some namespaces to clear only specific timers, e.g:
// first (optional) parameter for setTimeout/setInterval is namespace
var test1 = setTimeout('myNamespace', function(){/**/, 1000); // 'myNamespace' is current namespace used for test1 timeout
var test2 = setTimeout(function(){/**/, 1000); // no namespace used for test2 timeout
Again, clearAllTimeout(); will clear both timeouts. To clear only namespaced one, you can use:
clearAllTimeout('myNamespace'); // clearAllInterval('myNamespace'); for namespaced intervals
This will clear only test1 timeout
You could for some reason wish to delete non namespaced timeouts only. You could then use:
clearAllTimeout('no_ns'); // clearAllInterval('no_ns'); for non namespaced intervals only
This will clear only test2 timeout in this example
See jsFiddle DEMO
__END of EDIT__
Old post specific to opening question here:
You could try that:
var timeouts = [];
timeouts.push(setTimeout(function() {
social2();
}, 5000));
timeouts.push(setTimeout(function() {
social1();
}, 5000));
//etc...
function clearAllTimeouts(){
for(var i = 0, z = timeouts.length; i < z; i++)
clearTimeout(timeouts[i]);
timeouts = [];
}
UPDATED following David Thomas comment
var timeouts = {'social' : [], 'antisocial' : []};
//a social timeout
timeouts.social.push(setTimeout(function() {
social1();
}, 5000));
//an anti-social timeout
timeouts.antisocial.push(setTimeout(function() {
antisocial1();
}, 5000));
function clearTimeouts(namespace){
for(var i = 0, z = timeouts[namespace].length; i < z; i++)
clearTimeout(timeouts[namespace][i]);
timeouts[namespace] = [];
}
//usage e.g
clearTimeouts("social");
//Incase if you are looking for full fledged code
var dict = {};
function checkForIntervals(id){
var index = index;
var result = findOrAddProperty(id);
if(result.length != 0){
clearTimeoutsFor(id);
}
dict[id].push(setTimeout(function(){alertFunc(id,index);}, 60000));
};
// to clear specific area timeout
function clearTimeoutsFor(namespace){
for(var i = 0, z = dict[namespace].length; i < z; i++)
clearTimeout(dict[namespace][i]);
dict[namespace] = [];
}
to clear all timeouts
function clearAllTimeOuts(){
for (key in dict) {
for(var i = 0, z = dict[key].length; i < z; i++)
clearTimeout(dict[key][i]);
dict[key] =[];
}
};
function findOrAddProperty(str){
var temp = [];
for (key in dict) {
if(key == str){
if (dict.hasOwnProperty(key)) {
temp = dict[key];
break;
}
}
}
if(temp.length == 0){
dict[str] = [];
}
return temp;
};
function alertFunc(id,index) {
jQuery(document).ready(function($) {
do the ajax call here after 1 min
});
};

Simple function call inside module, getting NaN, huh?

Here is the module i am working on:
var FeatureRotator = (function($,global) {
var self = {},
currentFeature = 0,
images = [],
imagePrefix = "/public/images/features/",
timer = null,
totalImages = 0,
initialFeature,
interval,
blendSpeed,
element = null,
img1 = null,
img2 = null;
function setVisibleImage(iid) {
$("#img1").attr('src',images[iid].src).css('opacity',1);
$("#img2").css('opacity',0);
$(".active").removeClass("active");
$("#f"+iid).addClass("active");
}
function setCurrentImage(id) {
currentFeature = id;
setVisibleImage(id);
}
function doHoverIn(position) {
if (currentFeature === position) {
self.pause();
} else {
setCurrentImage(global.parseInt(position, 10));
self.pause();
}
}
function doHoverOut(position) {
self.unpause();
}
self.init = function(options,callback) {
var i = 0,
tempImg = null;
interval = options.interval || 5000;
blendSpeed = options.blendSpeed || 500;
element = options.element;
initialFeature = options.initialFeature || 0;
img1 = $("<img/>").attr('id','img1');
img2 = $("<img/>").attr('id','img2').css('opacity','0').css('margin-top',-options.height);
$(element).append(img1).append(img2);
totalImages = $(".feature").size();
for (i = 0;i < totalImages; i++) {
tempImg = new global.Image();
tempImg.src = imagePrefix +"feature_" + i + ".png";
images.push(tempImg);
$("#f"+i).css('background-image',
'url("'+imagePrefix+"feature_"+i+"_thumb.png"+'")')
.hover(doHoverIn($(this).attr('position'))
, doHoverOut($(this).attr('position'))
).attr('position',i);
}
setVisibleImage(initialFeature);
if (options.autoStart) {
self.start();
}
if (callback !== null) {
callback();
}
};
function updateImage() {
var active = $("#img1").css('opacity') === 1 ? "#img1" : "#img2";
var nextFeature = (currentFeature === totalImages-1 ? 0 : currentFeature+1);
if (active === "#img1") {
$("#img2").attr('src',images[nextFeature].src);
$("#img2").fadeTo(blendSpeed, 1);
$("#img1").fadeTo(blendSpeed, 0);
} else {
$("#img1").attr('src',images[nextFeature].src);
$("#img1").fadeTo(blendSpeed, 1);
$("#img2").fadeTo(blendSpeed, 0);
}
$("#f"+currentFeature).removeClass("active");
$("#f"+nextFeature).addClass("active");
currentFeature = nextFeature;
}
self.start = function() {
currentFeature = initialFeature;
setVisibleImage(currentFeature);
timer = global.setInterval(function(){
updateImage();
}, interval);
};
self.pause = function() {
global.clearTimeout(timer);
};
self.unpause = function() {
timer = global.setInterval(function(){
updateImage();
}, interval);
};
return self;
}(this.jQuery, this));
And here is how it is used on the page:
<script type="text/javascript">
// ...
$(function() {
FeatureRotator.init({
interval:5000,
element:'#intro',
autoStart:true,
height:177,
blendSpeed:1000,
initialFeature:0
});
});
</script>
The problem is, when setVisibleImage is called from the init method, the value of iid is NaN. I've stepped through the debugger and verified that 'initialFeature' is 0 when the setVisibleImage function is called, but alas, the value doesn't make it over there.
Can anyone help me determine what the problem is? I've run the code through JSLint, and it came back clean.
UPDATE
Ok here is my updated code, which works now except the fading doesnt work, the image just flips to the next one and doesn't fade smoothly anymore:
var FeatureRotator = (function($,global) {
var self = {},
currentFeature = 0,
images = [],
imagePrefix = "/public/images/features/",
timer = null,
totalImages = 0,
initialFeature = 0,
interval,
blendSpeed;
function setVisibleImage(iid) {
$("#img1").attr('src',images[iid].src).css('opacity',1);
$("#img2").css('opacity',0);
$(".active").removeClass("active");
$("#f"+iid).addClass("active");
}
function setCurrentImage(id) {
currentFeature = id;
setVisibleImage(id);
}
function doHoverIn(obj) {
var position = global.parseInt(obj.target.attributes["position"].value,10);
if (currentFeature === position) {
self.pause();
} else {
setCurrentImage(global.parseInt(position, 10));
self.pause();
}
}
function doHoverOut() {
self.unpause();
}
self.init = function(options,callback) {
var i = 0,
tempImg = null,
element = null,
img1 = null,
img2 = null;
interval = options.interval || 5000;
blendSpeed = options.blendSpeed || 500;
element = options.element;
initialFeature = options.initialFeature || 0;
img1 = $("<img/>").attr('id','img1');
img2 = $("<img/>").attr('id','img2').css('opacity','0').css('margin-top',-options.height);
$(element).append(img1).append(img2);
totalImages = $(".feature").size();
for (i = 0;i < totalImages; i++) {
tempImg = new global.Image();
tempImg.src = imagePrefix +"feature_" + i + ".png";
images.push(tempImg);
$("#f"+i).css('background-image','url("'+imagePrefix+"feature_"+i+"_thumb.png"+'")')
.hover(doHoverIn, doHoverOut)
.attr('position',i);
}
setVisibleImage(initialFeature);
if (options.autoStart) {
self.start();
}
if (typeof callback === "function") {
callback();
}
};
function updateImage() {
var active = $("#img1").css('opacity') === 1 ? "#img1" : "#img2";
var nextFeature = (currentFeature === totalImages-1 ? 0 : currentFeature+1);
if (active === "#img1") {
$("#img2").attr('src',images[nextFeature].src);
$("#img2").fadeTo(blendSpeed, 1);
$("#img1").fadeTo(blendSpeed, 0);
} else {
$("#img1").attr('src',images[nextFeature].src);
$("#img1").fadeTo(blendSpeed, 1);
$("#img2").fadeTo(blendSpeed, 0);
}
$("#f"+currentFeature).removeClass("active");
$("#f"+nextFeature).addClass("active");
currentFeature = nextFeature;
}
self.start = function() {
currentFeature = initialFeature;
setVisibleImage(currentFeature);
timer = global.setInterval(function(){
updateImage();
}, interval);
};
self.stop = function() {
global.clearTimeout(timer);
};
self.pause = function() {
global.clearTimeout(timer);
};
self.unpause = function() {
timer = global.setInterval(function(){
updateImage();
}, interval);
};
return self;
}(this.jQuery, this));
Since you're getting NaN, I'm guessing it is actually taking place from this line:
.hover(doHoverIn($(this).attr('position'))
...which calls this:
setCurrentImage(global.parseInt(position, 10)); // note the parseInt()
...which calls this:
setVisibleImage(id);
So the position being passed to parseInt is coming from $(this).attr('position'), which is likely an value that can't be parsed into a Number, so you get NaN.
Check out the value of that attribute in first line of the block for the for statement.
for (i = 0;i < totalImages; i++) {
console.log( $(this).attr('position') ); // verify the value of position
// ...

Categories

Resources