I've just started using JSLint to make sure that the JavaScript code I'm creating at least meets some standards and I've got a confusing message:
JSLint: Unexpected 'that'.
The code is part of a solution to implement a progress bar, part of which is this object that handles timers and callbacks and is as follows (this is an extract from the beginning of a larger file, I can add the whole file if required):
var ProgressHandler = function () {
"use strict";
// Build a new object
var that = {};
// Add basic properties
that.taskid = 0;
that.timerid = 0; // Timer ID used to push refreshes
that.progressUrl = ""; // URL to invoke to read progress
that.interval = 500; // The interval for progress refresh
that.taskProgressCallback = null; // The user-defined callback that refreshes the UI
that.taskCompletedCallback = null; // The user-defined callback that finalizes the call
// Set progress url
that.setProgressUrl = function (url) {
that.progressUrl = url;
return this;
}
// Set frequency of refresh
that.setInterval = function (interval) {
that.interval = interval;
return this;
};
The message appears on the line that begins that.setInterval. There are further uses of that I but JSLint also says that it stops processing at this point. I've tried searching for this message but it's not listed specifically here or on jslinterrors.com.
Why is this appearing and what can I do to fix it? Or should it just be ignored?
The issue seems to be that you do not have a ; after the definition of that.setProgressUrl. Changing to:
// Set progress url
that.setProgressUrl = function (url) {
that.progressUrl = url;
return this;
};
Resolves the reported issue. You then have an issue in that you are missing a closing brace and semi colon at the end of the file, not sure if that is just a copy and paste issue. The complete script should look like:
var ProgressHandler = function () {
"use strict";
// Build a new object
var that = {};
// Add basic properties
that.taskid = 0;
that.timerid = 0; // Timer ID used to push refreshes
that.progressUrl = ""; // URL to invoke to read progress
that.interval = 500; // The interval for progress refresh
that.taskProgressCallback = null; // The user-defined callback that refreshes the UI
that.taskCompletedCallback = null; // The user-defined callback that finalizes the call
// Set progress url
that.setProgressUrl = function (url) {
that.progressUrl = url;
return this;
};
// Set frequency of refresh
that.setInterval = function (interval) {
that.interval = interval;
return this;
};
};
Related
I understand that in D3, dispatch can be used to fire events to multiple visualisations according to this example.
I also understand that if I want to call a dispatch from an object and pass in the context, I can use apply as shown here.
However, I'm having a hard time combining the arguments from a D3 dispatch and the context that I want.
// create my dispatcher
var probeDispatch = d3.dispatch("probeLoad");
var line_count = 0;
// load a file with a bunch of JSON and send one entry every 50 ms
var lines = [[0,1],[1,2],[2,0]];
var parse_timer = window.setInterval(
function () {
parse_dispatch();
}, 50
);
function parse_dispatch(){
// send two arguments with my dispatch
probeDispatch.probeLoad(lines[line_count][0], lines[line_count][1]);
line_count += 1;
if(line_count >= lines.length){
//line_count = 0
window.clearInterval(parse_timer);
}
}
// my chart object
var genChart = function(label){
this.label = label;
// assume I've drawn my chart somewhere here
probeDispatch.on(("probeLoad."+this.label), this.probeParse);
// this next line isn't working, since the
// console.log in probeLoad still returns undefined
probeDispatch.probeLoad.apply(this);
};
genChart.prototype = {
probeParse: function(probeData, simTime) {
// How do I get the context from the object that's calling probeParse
// into the probeParse scope?
var self = this;
console.log(self.label);
}
};
new genChart("pants");
new genChart("shirt");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
It does set the context properly when you see "pants" in the console.
But then there are 3 undefined's logged, because you also call
// send two arguments with my dispatch
probeDispatch.probeLoad(lines[line_count][0], lines[line_count][1]);
without supplying context.
You need
probeDispatch.probeLoad.apply(instanceOfGenChart, [lines[line_count][0], lines[line_count][1]]);
But enabling that also requires moveing parse_dispatch down the page.
// create my dispatcher
var probeDispatch = d3.dispatch("probeLoad");
var line_count = 0;
// load a file with a bunch of JSON and send one entry every 50 ms
var lines = [[0,1],[1,2],[2,0]];
var parse_timer = window.setInterval(
function () {
parse_dispatch();
}, 50
);
// my chart object
var genChart = function(label){
this.label = label;
// assume I've drawn my chart somewhere here
probeDispatch.on(("probeLoad."+this.label), this.probeParse);
// this next line isn't working, but I don't know what to do
probeDispatch.probeLoad.apply(this);
};
genChart.prototype = {
probeParse: function(probeData, simTime) {
// How do I get the context from the object that's calling probeParse
// into the probeParse scope?
var self = this;
console.log(self.label);
}
};
var instanceOfGenChart = new genChart("pants");
function parse_dispatch(){
// send two arguments with my dispatch
probeDispatch.probeLoad.apply(instanceOfGenChart, [lines[line_count][0], lines[line_count][1]]);
line_count += 1;
if(line_count >= lines.length){
//line_count = 0
window.clearInterval(parse_timer);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
So it turns out to bring the context into the function, I have to bind() it for reasons I'm not too clear on.
// create my dispatcher
var probeDispatch = d3.dispatch("probeLoad");
var line_count = 0;
// load a file with a bunch of JSON and send one entry every 50 ms
var lines = [[0,1],[1,2],[2,0]];
var parse_timer = window.setInterval(
function () {
parse_dispatch();
}, 50
);
function parse_dispatch(){
// send two arguments with my dispatch
probeDispatch.probeLoad(lines[line_count][0], lines[line_count][1]);
line_count += 1;
if(line_count >= lines.length){
//line_count = 0
window.clearInterval(parse_timer);
}
}
// my chart object
var genChart = function(label){
this.label = label;
// assume I've drawn my chart somewhere here
probeDispatch.on(("probeLoad."+this.label), this.probeParse.bind(this));
};
genChart.prototype = {
probeParse: function(probeData, simTime) {
// How do I get the context from the object that's calling probeParse
// into the probeParse scope?
var self = this;
console.log(self.label);
}
};
new genChart("pants");
new genChart("shirt");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Added by meetamit
Bind is the solution here, because it locks a scope to an "instance" of genChart.prototype. probeParse. This way parse_dispatch (the invoker) doesn't need to know anything about scope. It's equivalent to this:
// my chart object
var genChart = function(label){
this.label = label;
var self = this;
var probeParseBound = function() { self.probeParse(); };
probeDispatch.on(("probeLoad."+this.label), probeParseBound);
};
I was wondering if I can stop a function from even defining itself (basically I'm not using it, but it still defines itself, at least that's what I assume the problem is).
if (document.getElementById("loginLogoutButton").title!="התנתק/י") {
document.getElementById("username").value="asdf";
document.getElementById("password").value="asdf";
var target = document.getElementById("loginLogoutButton");
var clickevent = document.createEvent("MouseEvents");
clickevent.initEvent("click", true, true);
target.dispatchEvent(clickevent);
}
function loaded() {
var targLpink = document.getElementById ("iconImage_3");
var clickEvent = document.createEvent ("MouseEvents");
clickEvent.initEvent('dblclick', false, true);
targLpink.dispatchEvent(clickEvent);
}
window.addEventListener("load", setTimeout(loaded,3000));
var msgodd;
var msgeven;
var messages;
//The first error happens here as far as I can tell
//"Uncaught TypeError: undefined is not a function"
function list() {
msgeven = document.getElementsByTagName("even unread");
msgodd= document.getElementsByTagName("odd unread");
var k = msgodd.length+msgeven.length;
confirm(k);
var i = 0;
while ((i+2)< k) {
if (i%2==0 && i+2<msgeven.length){
messages.push(msgeven[i/2].id);
}
if(i%2==1 && i+2<msgodd.length){
messages.push(msgodd[(i-1)/2].id);
}
i=i+1;
}
alert(messages.length);
}
setTimeout(list, 9000);
I then get an error on tagname:
Uncaught TypeError: Cannot read property 'length' of undefined
I might also throw out that I have to use the setTimeout since I'm waiting for the page to load (it's not a different address, it opens up stuff within itself, so I can't use onload on that - at least when i tried it failed).
You're treating msgeven/msgodd as an array. But it's a NodeList! You can convert them with this:
msgeven = Array.prototype.slice.call(document.getElementsByClassName("even unread"));
msgodd = Array.prototype.slice.call(document.getElementsByClassName("odd unread"));
I want to detect if the method of an object is called.
I have a video player in my page and when it is done playing, I want to show some contents.
For example:
function videoSet(){
var instance = this;
this.video = $('#video')
this.video.bind("ended", function() {
instance.endVideo()
});
}
videoSet.prototype.endVideo = function(){
$('#test1').css('visibility','visible');
}
//more methods...
function main(){
this.init();
}
main.prototype.init = function(){
this.video = new videoSet() //init an video object.
// more code...
//I need to know if the video is ended...
}
var mainObj = new main();
Inside my endVideo method, I have $('#test1').css('visibility','visible'); but I have so much code in my main object and I want to be able to detect if the video has ended in my main object.
Is that possible?
You can have multiple eventListeners on DOM objects...
var Video = function () { this.video = document.querySelector("#my-video"); };
var Main = function () {
var myVideo = new Video();
myVideo.video.addEventListener("ended", function () { console.log("It's over!"); });
myVideo.video.addEventListener("ended", function () {
console.log("Play something else.");
});
};
Main();
There's nothing stopping you from adding an event-listener to the object from inside of main.
Moreover, this leads to custom event systems -- Publisher/Subscriber or Observer or "Emitters".
If you can implement one of these, on an object, then your object can create/fire custom events, and pass custom data, and any time you have access to that object, you can subscribe (as long as you know what the events are called, and how to handle the data you will get back).
For example, you might want to have a video-playing system that loads the next film (or a countdown screen, until the next film, et cetera, for continuous playback, with a playlist that highlights the current film).
var VideoPlayer = function (id) {
var player = this;
player.video = document.getElementById(id);
// attach an emitter-system with "on", "off" and "emit", or whatever you choose
addEmitter(player);
player.load = function (video) { player.video.src = video.src; };
player.init = function () {
player.video.addEventListener("ended", function () {
// fire custom-event
player.emit("video-ended");
});
player.video.addEventListener("canplay", function () {
// auto-play video, fire event
player.video.play();
player.emit("video-playing");
});
};
},
VideoPlaylist = function (id, videos) {
var playlist = this;
playlist.root = document.getElementById(id);
playlist.videos = videos;
playlist.addVideo = function (video) { /* attach each video to the root */ };
playlist.currentVideoIndex = 0;
playlist.currentVideo = playlist.videos[playlist.currentVideoIndex];
playlist.select = function (i) {
playlist.currentVideoIndex = i;
playlist.currentVideo = playlist.videos[i];
// fire a custom event
playlist.emit("load-video", playlist.currentVideo);
};
playlist.nextVideo = function () {
var i = (playlist.currentVideoIndex + 1) % playlist.videos.length; // loops
playlist.select(i);
};
addEmitter(playlist);
};
var Main = function () {
var video_player = new VideoPlayer("my-player"),
video_playlist = new VideoPlaylist("my-playlist", [{ src : "...", title : "A" }, { src : "...", title : "B" }]);
video_player.on("video-ended", video_playlist.next);
video_playlist.on("load-video", video_player.load );
// add another listener for another component, to handle on-screen controls
video_player.on("video-playing", video_controller.show_playing);
// add another listener for another component, to display the data about the video
video_playlist.on("load-video", video_description.display);
// add another listener for another component to load comments
video_playlist.on("load-video", video_comments.load);
};
Main();
This isn't a particularly Java-like way of writing programs, but JavaScript isn't particularly Java-like (though you can make it look similar).
You'll notice that inside of the Main function all I'm doing is wiring behaviours together, rather than writing out custom logic.
Of course, you can take this way further...
...and I haven't shown you how my emitter is made, but they're not hard to make, either.
Publisher/Subscriber or Observer or Emitter implementations are great practice for JS (and very easy in JS compared to other languages).
But as you can see, with a little thinking, this is a really simple and versatile way of dispatching code.
You can use an ended flag in the videoSet object like
function videoSet() {
var instance = this;
this.ended=false;
this.video = $('#video')
this.video.bind("ended", function () {
instance.endVideo()
});
}
videoSet.prototype.endVideo = function () {
$('#test1').css('visibility', 'visible');
this.ended=true;
}
videoSet.prototype.isEnded = function () {
return this.ended;
}
//more methods...
function main() {
this.init();
//later
if(myVideoSet.isEnded()){
console.log('completed')
}
}
I am facing a problem with setInterval being used in a loop.
I have a function subscribeFeed( ) which takes an array of urls as input.
It loops through the url array and subscribes each url to getFeedAutomatically() using a setInterval function.
so if three URL's are there in the array, then 3 setInterval's will be called.
The problem is
1)how to distinguish which setInterval is called for which URL.
2)it is causing Runtime exception in setInterval( i guess because of closure problem in javascript)
//constructor
function myfeed(){
this.feedArray = [];
}
myfeed.prototype.constructor= myfeed;
myfeed.prototype.subscribeFeed =function(feedUrl){
var i=0;
var url;
var count = 0;
var _this = this;
var feedInfo = {
url : [],
status : ""
};
var urlinfo = [];
feedUrl = (feedUrl instanceof Array) ? feedUrl : [feedUrl];
//notifyInterval = (notifyInterval instanceof Array) ? notifyInterval: [notifyInterval];
for (i = 0; i < feedUrl.length; i++) {
urlinfo[i] = {
url:'',
notifyInterval:5000,// Default Notify/Refresh interval for the feed
isenable:true, // true allows the feed to be fetched from the URL
timerID: null, //default ID is null
called : false,
position : 0,
getFeedAutomatically : function(url){
_this.getFeedUpdate(url);
},
};
urlinfo[i].url = feedUrl[i].URL;
//overide the default notify interval
if(feedUrl[i].NotifyInterval /*&& (feedUrl[i] !=undefined)*/){
urlinfo[i].notifyInterval = feedUrl[i].NotifyInterval;
}
// Trigger the Feed registered event with the info about URL and status
feedInfo.url[i] = feedUrl[i].URL;
//Set the interval to get the feed.
urlinfo[i].timerID = setInterval(function(){
urlinfo[i].getFeedAutomatically(urlinfo[i].url);
}, urlinfo[i].notifyInterval);
this.feedArray.push(urlinfo[i]);
}
}
// The getFeedUpate function will make an Ajax request and coninue
myfeed.prototype.getFeedUpdate = function( ){
}
I am posting the same on jsfiddle
http://jsfiddle.net/visibleinvisibly/S37Rj/
Thanking you in advance
After some prototyping i found a answer ,which has the answer,move the closure outside
function myclass(){
}
myclass.prototype.funone= function(){
var counter =0;
var timerID;
timerID = setInterval( function(){
alert(counter++);
},1000);
}
myclass.prototype.funtwo= function(){
var timerID2;
var counter2 =50;
timerID2 = setInterval( function(){
alert(counter2++);
},2000);
}
myclass.prototype.funthree = function( ){
var urlArray =["google.com","yahoo.com"];
var timeArray =[15000,6000];
var timerID ;
for(var i=0;i<2; i++){
var url = urlArray[i];
var timerinterval = timeArray[i];
timerID = this.register( url,timerinterval);
}
}
myclass.prototype.register = function(url,timerInterval){
var myUrl =url;
var myTimer = timerInterval;
var timerID = setInterval( function(){
alert(myUrl+"with"+ myTimer);
},myTimer);
}
var m = new myclass( );
m.funthree( );
http://jsfiddle.net/visibleinvisibly/Q4SBG/13/
The move the index binding from the setInterval and pass the url and time interval.
It works perfectly
You might want to have a look at this answer (under "The this variable" at the bottom) about what the this value means.
The error in your code may have something to do with using a counter in a loop and creating closures depending on the counter. The simplest way to create such closures is.
for(i=0;i<len;i++){
object.myCallback = (function(counter){
return function(){
doSomethingWith(counter);
}
}(i));
}
When creating closures on the fly like that you should be careful not dragging large or large amounts of variables into the closure scope. The link above and code below shows how to do this safely.
I've changed some of the code to make it simpler and not copy stuff that doesn't need to be copied, the setInterval is setTimeout so it only does it once but it's the same idea.
//constructor
function MyFeed(){
this.feedArray = [];
}
MyFeed.prototype.subscribeFeed =function(feedUrl){
var i=0,urlInfo=[];
feedUrl = (feedUrl instanceof Array) ? feedUrl : [feedUrl];
for (i = 0; i < feedUrl.length; i++) {
feedUrl[i].isEnable=true;
feedUrl[i].called=false;
feedUrl[i].position=0;//not sure what this is supposed to do
//Set the interval to get the feed.
feedUrl[i].timerID = setTimeout(this.closures//changed this to timeout
.getFeedUpdate(this)
,feedUrl[i].notifyInterval||100//changed default value
);
this.feedArray.push(feedUrl[i]);
}
};
// The getFeedUpate function will make an Ajax request and coninue
MyFeed.prototype.getFeedUpdate = function( index ){
console.log("in getFeedUpdate, this is now:",this);
console.log("my feed url object:",this.feedArray[index].url);
};
//limit closure scope, define closure creators here
MyFeed.prototype.closures={
//this.closures.getFeedUpdate(this)
// will return a closure that calls this.getFeedUpdate
// with correct parameters
getFeedUpdate:function(me){
var index = me.feedArray.length;
return function(){
me.getFeedUpdate(index);
};
}
};
//code to test adding single feed
var mf = new MyFeed();
mf.subscribeFeed({
url:"I am last",
notifyInterval:1000
});
//add another single feed
mf.subscribeFeed({
url:"first.com"
});
//and another
mf.subscribeFeed({
url:"second.com"
});
//and add some more feeds in an array of feeds
mf.subscribeFeed([
{
url:"third"
},
{
url:"fifth"
},
{
url:"no, I am last",
notifyInterval:1500
}
]);
Try FireFox with the FireBug plugin or Chrome and press F12 to see the console, when the log statements log something you can click on it to see the details of the logged item. Very helpful to log objects like this or simple values like index
I am facing a weird issue with "this". I have the code as follow which is for a page. In this I am creating a page and binding the onclick events. Here I reset the self[key] everytime I call funcOne but onClick I am setting the data.
The second time when I call funcOne and again I call onClick the data in this[key] is the old data instead of resetting.
Please suggest if I am doing anything wrong here. I am new to Javascript.
classExample = function(page) {
someBaseClass.call(this, page);
}
classExample.prototype.funcOne = function() {
var self = this;
var callback = function(data) {
self[key] = []; //based on some logic I am setting
};
model.getData(callback);
someButton.onClick = function() {
self.funcTwo();
};
};
classExample.prototype.funcTwo = function() {
//function from other class is called and data to this[key] is set
classTwo.someMethod(this);
var savedData = this[key];
};
var obj = new classExample(page);
obj.funcOne();
//after this I invoke onClick event
PS : I am not using any third party library.