I am trying to overwrite timer methods (setTimeout, clearTimeout, setInterval and clearInterval) on an MSHTML instance, so I can track functions that have been added to setTimeout/setInterval and block the script from exiting until those functions have been called or removed.
I am getting a Permission denied error when the original setTimeout is called within the wrapper setTimeout function:
var window = new ActiveXObject('htmlfile').parentWindow.window;
var _setTimeout = window.setTimeout;
var timeouts = {};
setTimeout = function (code, ms, lang) {
//script fails here - Permission denied
var id = _setTimeout(code, ms, lang || 'javascript');
timeouts[id] = true;
return id;
};
setTimeout(function () {
window.alert('Timed function');
}, 1000);
var testTimeouts = function () {
var i;
for (i in timeouts) {
if (timeouts[i]) {return false;}
}
};
while (!testTimeouts()) {
WScript.Sleep(100);
}
window.alert('At end of script');
I can pass code to the original setTimeout before it's been overwritten:
window.setTimeout(function () {
window.alert('Timed function');
}, 1000);
WScript.Sleep(2500);
window.alert('At end of script');
Preserving the original context (as suggested in an answer that seems to have vanished) doesn't work:
window._setTimeout = window.setTimeout;
var timeouts = {};
window.setTimeout = function (code, ms, lang) {
var id = window._setTimeout(code, ms, lang || 'javascript');
timeouts[id] = true;
return id;
};
window.setTimeout(function () {
window.alert('Timed function');
}, 1000);
It fails at the call to window._setTimeout with Object doesn't support this property or method.
I should note that I am running IE8, WinXP SP3, JScript 5.8.
Why don't you call setTimeout from the window that you have stored?
var setTimeout;
var timeouts = {};
(function() {
var window = new ActiveXObject('htmlfile').parentWindow;
setTimeout = function(code, delay, lang){
var id = window.setTimeout(code, delay, lang || 'javascript');
timeouts[id] = true;
return id;
};
})();
Related
I am having a hard time getting a countdown timer working as I don't know what I am doing wrong. I am trying to setup a countdown timer using jQuery in a prototype.
The main problem I see so far is at the setInterval:
_self.counter = setInterval(_self.runTimer(_self),1000);
When I don't pass in the "this" I get NaN but when I do the countdown only happens once and then stops.
Here is my JSFiddle work so far:
http://jsfiddle.net/f9GN7/
Thank you in advance.
I've modified a little of your code, I changed setInterval to setTimeout.
var timer_code = function(){
this.counter;
this.timeCountDown = 30;
}
timer_code.prototype = {
init : function(){
var _self = this;
$('#start').on('click',function(e){
_self.setTimer();
});
},
setTimer : function(){
var _self = this;
// _self.counter = setInterval(_self.runTimer(_self),1000);
var timerLoop = function(){
if(_self.timeCountDown > 0){
_self.runTimer();
setTimeout(timerLoop, 1000);
}
};
timerLoop();
},
runTimer : function(){
var _self = this;
_self.timeCountDown--;
if(_self.timeCountDown <= 0){
// clearInterval(_self.counter);
$('#timer').html("DONE");
return;
}
$('#timer').html(_self.timeCountDown);
console.log(_self.timeCountDown);
}
}
var timer = new timer_code();
timer.init();
http://jsfiddle.net/f9GN7/1/
setInterval gets a function reference as its first parameter ..
This function may not return a function object, the function call you just passed needs to be called in the scoope of a closure
Keeping your code with just a few modifications :
setTimer: function(){
if(this.counter)
clearInterval(this.counter); // timer may have already been launched, it may need to be cleared if its value is an integer and is != 0
this.counter = setInterval(
(function (ref) {
return function () {
ref.runTimer();
}
})(this),
1000);
}
See Fiddle Here
Let's assume that I have the timeout ID returned from setTimeout or setInterval.
Can I get, in some way, the original function or code, associated with it?
Something like this:
var timer_id = setTimeout(function() {
console.log('Hello Stackoverflowers!');
}, 100000);
var fn = timer_id.get_function(); // desired method
fn(); // output: 'Hello Stackoverflowers!'
You can put a wrapper around setTimeout - I just threw this one together (after a few iterations of testing...)
(function() {
var cache = {};
var _setTimeout = window.setTimeout;
var _clearTimeout = window.clearTimeout;
window.setTimeout = function(fn, delay) {
var id = _setTimeout(function() {
delete cache[id]; // ensure the map is cleared up on completion
fn();
}, delay);
cache[id] = fn;
return id;
}
window.clearTimeout = function(id) {
delete cache[id];
_clearTimeout(id);
}
window.getTimeout = function(id) {
return cache[id];
}
})();
NB: this won't work if you use a string for the callback. But no one does that, do they..?
Nor does it support passing the ES5 additional parameters to the callback function, although this would be easy to support.
var timeouts = {}; // hold the data
function makeTimeout (func, interval) {
var run = function(){
timeouts[id] = undefined;
func();
}
var id = window.setTimeout(run, interval);
timeouts[id] = func;
return id;
}
function removeTimeout (id) {
window.clearTimeout(id);
timeouts[id]=undefined;
}
function doTimeoutEarly (id) {
func = timeouts[id];
removeTimeout(id);
func();
}
var theId = makeTimeout( function(){ alert("here"); }, 10000);
console.log((timeouts[theId] || "").toString());
timeouts[theId](); // run function immediately, will still run with timer
You can store each timeout function in an object so that you can retrieve it later.
var timeout_funcs = {};
function addTimeout(func,time) {
var id = window.setTimeout(func,time);
timeout_funcs[id] = func;
return id;
}
function getTimeout(id) {
if(timeout_funcs[id])
return timeout_funcs[id];
else
return null;
}
function delTimeout(id) {
if(timeout_funcs[id]) {
window.clearTimeout(timeout_funcs[id]);
delete timeout_funcs[id];
}
}
the IDs returned from setTimeout/setInterval are just numbers, they have no properties or methods other than those that every other number would have. If you want to get that function, you can declare it first instead of using an anonymous:
var myFunc = function() {
console.log('Hello Stackoverflowers!');
};
var timer_id = setTimeout(myFunc, 100000);
myFunc(); // output: 'Hello Stackoverflowers!'
clearTimeout(timer_id); // unless you want it to fire twice
I have the following HTML page:
<html>
<script>
var global = {};
global.obj = {
// when called this function will cause 'hello' to be output to the
// console every 1 second
repeat: function () {
setInterval(function () {
console.log('hello');
}, 1000);
}
}
global.obj.repeat();
global.obj = [];
// even after we overwrite global.obj, 'hello'
// continues to be output to the console every second
</script>
</html>
I want to write a function similar to repeat, except when global.obj is overwritten, setInterval will stop being called
You'll want to use getters/setters, Mozilla has some good docs on this.
You may have to tweak it a bit:
var intervalRef = null;
var global = {objRef: {}};
global.__defineSetter__("obj", function(o) {
if (intervalRef)
clearInterval(intervalRef);
intervalRef = null;
global.objRef = o;
});
global.__defineGetter__("obj", function() {
return global.objRef;
});
global.obj = {
repeat: function () {
intervalRef = setInterval(function () {
console.log('hello');
}, 1000);
}
}
global.obj.repeat();
setTimeout(function() { //this just demonstrates that you can let it run for 5 secs before clearing the timer.
global.obj = [];
}, 5000);
I tested this and verified that it works.
See this Fiddle:
// html
<p id="stopper">Click</p>
// js
var counter = new Object();
counter.timer = setInterval( function(){
console.log("Hello!");
}, 1000 );
$("#stopper").click(function(){
console.log("Stopping");
clearInterval(counter.timer);
});
I am using debouncing to execute events after a timeout using settimeout. The problem I have, is that other javascript events expect those events to occur synchronously. Since they are now executing after a timeout, I'd like to be able to trigger them prematurely by other javascript events (so those events requiring them won't fail).
Anywhom, if I do something like:
timeout = setTimeout(function() { alert('hi'); }, 10000);
, and I want that to occur before 10 seconds passes, how can I do that?
The solution can involve jquery if necessary. Thanks!
Edit:
Is it possible to do this with just access to the timeout object?
So, if you make whatever you're delaying its own function:
function sayHi() {
alert('hi');
}
You can use a timeout and a regular function call:
var timeout = setTimeout(sayHi, 10000); // say hi after 10 seconds
Or to call it before the timeout expires, just call the function whenever you need to:
sayHi();
Am I on the right track here? If you need to cancel the timeout, call clearTimeout() on your timeout variable.
if (timeout)
clearTimeout(timeout);
You cannot track it with the standard setTimeout, but Javascript allows you to enhance features as you wish.
For example you could have your own enhanced setTimeout:
var _setTimeout = window.setTimeout;
var timeouts = [];
window.setTimeout = function(fn, ms) {
var id = _setTimeout(fn, ms);
timeouts[id] = fn;
return id;
};
window.premature = function(id) {
var fn = timeouts[id];
if (fn) {
clearTimeout(id);
if (fn instanceof String) {
eval(fn);
} else {
fn()
}
}
};
function printDate(str) {
$("body").append("<p>" + str + ". " + new Date() + "</p>");
}
$(function() {
var id1 = setTimeout(function() { printDate("id1"); }, 10000);
var id2 = setTimeout(function() { printDate("id2"); }, 10000);
printDate("first one");
// just to demonstrate that the string version works too
setTimeout("window.premature(" + id1 +")", 5000);
});
You can see it in action at jsFiddle
Do note, that this simple hack does not take into account clearing used ids when the timeouts do occur, but it is just to show that you can do this sort of thing in Javascript if you really need it.
polyfill solution
Here is some javascript that I've updated from a previous project, it is extended now with trigger and update methods; it is similar to Jan Wikholm's solution (+1) — but a bit more complete, taking into account clears, passing of arguments, and the prevention of eval if required:
(function(keep){
/// a few things to remember
keep.setTimeout = window.setTimeout;
keep.clearTimeout = window.clearTimeout;
keep.TO = function(){};
keep.list = {};
keep.settings = {
eval: false /// set this to true if you wish to support string timeouts
};
/**
* Quick check function to prevent eval
*/
keep.checkParam = function( param ){
if ( !keep.settings.eval && typeof param == 'string' ) {
throw new Error('setTimeout blocked evaluation of string, ' +
'use a function instead.');
return false;
}
else if ( param ) {
return true;
}
};
/**
* Simple function constructor to avoid trapping unwanted references
*/
keep.makeFunction = function(data){
return function(args){
/// copy our args array
args = data.args.slice();
/// do we allow eval?
if ( keep.settings.eval ) {
/// if so, reuse setTimeout for it's abilities
args[0] = data.param; /// use the original param
args[1] = 0; /// trigger immediately
keep.setTimeout.apply( window, args );
}
// more secure, assume dealing with function -- not string
else if ( keep.checkParam( data.param ) && data.param.apply ) {
data.param.apply( window, args.slice(2) );
}
else {
throw new Error('unsupported param for setTimeout' +
' i.e. non-function used.');
}
/// clear our storage of this tid
window.clearTimeout( data.tid );
};
};
/**
* Sets timeouts just like you would expect
*/
window.setTimeout = function( param, timeout ){
if ( keep.checkParam( param ) ) {
var tid, data;
/// support passing a timeout object as param
if ( param instanceof keep.TO ) {
data = param;
data.args[1] = data.timeout;
}
else {
/// create an object to store the timeout info
data = new keep.TO();
data.func = keep.makeFunction(data);
data.param = param;
data.timeout = timeout;
data.args = Array.prototype.slice.call(arguments,0);
data.args[0] = data.func;
}
data.tid = keep.setTimeout.apply( window, data.args );
keep.list[data.tid] = data;
/// enhance the returned number to support .clear, .trigger and .update
tid = new Number(data.tid);
tid.clear = window.clearTimeout;
tid.trigger = window.triggerTimeout;
tid.update = window.updateTimeout;
return tid;
}
};
/**
* Clearing timeouts since 2013
*/
window.clearTimeout = function( tid ){
if ( this instanceof Number ) {
tid = 0 + this;
}
var obj;
if ( (obj = window.getTimeout(tid)) ) {
delete keep.list[tid];
keep.clearTimeout.call(window, tid);
}
};
/**
* Returns the internal timeout storage object
*/
window.getTimeout = function( tid ){
var obj;
if ( (obj = keep.list[tid]) ) {
return obj;
}
};
/**
* Clears and fires a timeout before it's outed time
*/
window.triggerTimeout = function( tid ){
if ( this instanceof Number ) {
tid = 0 + this;
}
var obj;
if ( (obj = window.getTimeout(tid)) ) {
window.clearTimeout(tid);
obj.func.call(window);
}
else {
throw new Error('No Timeout found to trigger for ID '+ tid);
}
};
/**
* Clears and recreates an existing timeout, returns a new timeout id.
*/
window.updateTimeout = function( tid, timeout ){
if ( this instanceof Number ) {
if ( arguments.length == 1 ) {
timeout = tid;
}
tid = 0 + this;
}
var obj;
if ( (obj = window.getTimeout(tid)) ) {
obj.timeout = timeout;
window.clearTimeout(tid);
return window.setTimeout(obj);
}
else {
throw new Error('No Timeout found to update for ID ' + tid);
}
};
/**
* Utility function to tidy up
*/
window.clearAllTimeouts = function(){
for ( var i in keep.list ) {
window.clearTimeout(i);
};
};
/// Tidy up
window.onunload = (function(previous){
return function(){
window.clearAllTimeouts();
keep.list = {};
previous && previous.call(window);
};
}(window.onunload));
})({});
include
Just put the above in a js file and include into your page using a normal script tag, the code does not need to be invoked in any way:
<script src="timeouts.js"></script>
usage
Obviously this should be used just like a normal setTimeout call, however you now have extra methods that should give more flexibility.
var tid = setTimeout( function(){ alert('OK Computer') }, 2000 );
For example you could cancel the original and force the timeout to trigger earlier:
setTimeout( function(){ triggerTimeout( tid ); }, 500 );
Or, you could update the timeout (make sure we remember the new returned tid):
setTimeout( function(){ tid = updateTimeout( tid, 5000 ); }, 500 );
You can also do the usual:
setTimeout( function(){ clearTimeout( tid ); }, 1000 );
Each of these methods are also accessible via the tid itself:
setTimeout( function(){ tid.trigger(); }, 1000 );
setTimeout( function(){ tid.update( 5000 ); }, 1000 );
setTimeout( function(){ tid.clear(); }, 1000 );
By default this code prevents the use of setTimeout with a string param, mainly because it is a far better coding style to pass functions rather than strings. To change this you can switch the following setting to true:
keep.settings = {
eval: true
};
This is not recommended however.
There is also an added benefit to leaving eval disabled, in the fact that the code will use normal function calling to trigger the timeout i.e. .apply(). This means that whatever browser you are using, you can pass arguments to the timeout function via setTimeout — which is not normally something you can rely on cross-browser. e.g:
setTimeout( function(a){ alert(a) }, 2000, 'Hello World' );
Just put the function out and give it a name:
function handler(){
alert('hi');
}
timeout = setTimeout(handler, 10000);
then you can call it in other places with handler();
Use clearTimeout and reschedule for an earlier time.
its a server side Javascript (rhino engine), so setTimeout is not available. how to run a function asynchronously?
You can use java.util.Timer and java.util.TimerTask to roll your own set/clear Timeout and set/clear Interval functions:
var setTimeout,
clearTimeout,
setInterval,
clearInterval;
(function () {
var timer = new java.util.Timer();
var counter = 1;
var ids = {};
setTimeout = function (fn,delay) {
var id = counter++;
ids[id] = new JavaAdapter(java.util.TimerTask,{run: fn});
timer.schedule(ids[id],delay);
return id;
}
clearTimeout = function (id) {
ids[id].cancel();
timer.purge();
delete ids[id];
}
setInterval = function (fn,delay) {
var id = counter++;
ids[id] = new JavaAdapter(java.util.TimerTask,{run: fn});
timer.schedule(ids[id],delay,delay);
return id;
}
clearInterval = clearTimeout;
})()
Have a look at the Multithreaded Script Execution example on the Rhino Examples page. Basically, JavaScript does not support threading directly, but you may be able to use a Java thread to achieve what you are looking for.
Another version using ScheduledThreadPoolExecutor, compatible with Rhino 1.7R4 and proposed by #Nikita-Beloglazov:
var setTimeout, clearTimeout, setInterval, clearInterval;
(function () {
var executor = new java.util.concurrent.Executors.newScheduledThreadPool(1);
var counter = 1;
var ids = {};
setTimeout = function (fn,delay) {
var id = counter++;
var runnable = new JavaAdapter(java.lang.Runnable, {run: fn});
ids[id] = executor.schedule(runnable, delay,
java.util.concurrent.TimeUnit.MILLISECONDS);
return id;
}
clearTimeout = function (id) {
ids[id].cancel(false);
executor.purge();
delete ids[id];
}
setInterval = function (fn,delay) {
var id = counter++;
var runnable = new JavaAdapter(java.lang.Runnable, {run: fn});
ids[id] = executor.scheduleAtFixedRate(runnable, delay, delay,
java.util.concurrent.TimeUnit.MILLISECONDS);
return id;
}
clearInterval = clearTimeout;
})()
Reference: https://gist.github.com/nbeloglazov/9633318