<<script>>: bad evaluation: unexpected token var

So I found this matrix canvas and I’m trying to implement the javascript code in my starting passage using the <<script>> macro, but it doesn’t work. It has this red error message saying: <<SCRIPT>>: BAD EVALUATION: UNEXPECTED TOKEN VAR. It works just fine in the story Javascript section, but when I put the code in a passage it doesn’t work at all.

This isn’t the first time I’ve encountered this <<script>> error either. Almost every time I try to use it on any sugarcube story, I get the error.

Here’s the starting passage’s content:

<div id="container"><canvas id="matrix"></canvas></div>
<div class="title"><img height="60px" src="https://i.imgur.com/28xuG9j.png"></div>



[[Start New Game->Start]]
<<nobr>>
<<script>>
'use strict'

var matrix = document.getElementById('matrix');
var ctx = matrix.getContext('2d');

var config = {
  amount: 70,  // of columns going down (it will top at this number)
  speed: 100,   // time between updates in ms (lower = faster)
  size: 20,    // in px
  minLength: 5,
  maxLength: 11,
  firstColor: '#fff',
  color: 'red'
}

var datarray = [];


var width = ctx.canvas.width = window.innerWidth;
var height = ctx.canvas.height = window.innerHeight;


/*
Data colum object
=============
*/
function Data(x) {
  this.x = x
  this.y = 0
  this.history = []
  this.historySizeMax = Math.floor(Math.random() * config.maxLength + config.minLength)
}

Data.prototype = {
  update() {
    this.y += config.size
    if (this.y >= height + this.historySizeMax * config.size) {
      datarray.splice(datarray.indexOf(this), 1)
      putData()
    }

    this.history.unshift(String.fromCharCode(60 + Math.floor(Math.random() * 62)))
    if (this.history.length > this.historySizeMax) this.history.pop()
  },

  draw() {
    if(Math.random() > 0.995) return
    
    ctx.fillStyle = config.firstColor
    ctx.fillText(this.history[0], this.x, this.y)

    ctx.fillStyle = config.color
    for (var i = 1, char; char = this.history[i]; i++) {
      ctx.fillText(char, this.x, this.y - i * config.size)
    }
  }
  
}

var count = Math.floor(width / config.size)

function putData() {
  var newX = Math.floor(Math.random() * count) * config.size

  for (var i = 0, row; row = datarray[i]; i++) {
    if(row.x === newX && row.y - row.historySizeMax * config.size + config.size < config.size) return
  }
  datarray.push(new Data(newX))
}

/*
Init & loop
=============
*/

ctx.font = config.size + 'px Monospace'
ctx.shadowOffsetX = 1
ctx.shadowOffsetY = 1
ctx.shadowBlur = 6
ctx.shadowColor = config.color

setInterval(function() {
  ctx.clearRect(0, 0, width, height)

  if (datarray.length < config.amount) putData()

  for (var i = 0, column; column = datarray[i]; i++) {
    column.update()
    column.draw()
  }
  
}, config.speed)
	

<</script>>
<</nobr>>

Try taking out the <<nobr>> tags around your <<script>> tag? That wraps all your javascript code onto one line. And newlines often matter in JavaScript. You can write JS with semicolons separating every statement, and many people do, but some people leave them out (this code leaves out some of them) and then it needs the line breaks.

The problem is here:

var config = {
  amount: 70,  // of columns going down (it will top at this number)
  speed: 100,   // time between updates in ms (lower = faster)
  size: 20,    // in px
  minLength: 5,
  maxLength: 11,
  firstColor: '#fff',
  color: 'red'
}

var datarray = [];

Because there isn’t a semicolon after the }, the following var is not expected.

EDIT: Yeah, looks like there’s a lot more missing than that one.

^ That. The <<script>> macro is silent by default—i.e., it generates no output—so wrapping it in a <<nobr>> macro is both pointless and breaks the script because it lacks semi-colons in many places—one of which was pointed out by HiEv.

Your JavaScript is also trying to reference the <canvas id="matrix"> element, and that element won’t get added to the page’s DOM until after all the contents of the current passage has been processed.

eg. Even if you remove the <<nobr>> macro and add all the missing semi-colons you will still receive a “bad evaluation: Cannot read property ‘getContext’ of null” error message.

You will need to delay the execution of that JavaScript until after the contents of the current passage has finished being processed. Two common ways to do this are:
a. call the JavaScript from one of the SugarCube Events/Tasks that occur late in/at the end of the passage processing process.
b. Wrap the existing code in a JavaScript setTimeout() function.

FYI - You’ll need to put macro names within “preformatted text” markers to make them visible in this forum. You’ve missed that in a couple of other places as well, such as this one.

@KrypticKitty: You can use code like the following to do what Greyelf suggests:

<<script>>
$(document).one(":passagerender", function(ev) {
	var matrix = $(ev.content).find("#matrix")[0];
	(The rest of the code goes below...)
});
<</script>>

Hope that helps! :slight_smile:

I followed everyone’s advice, and it works now! However your task code did not work for me, so instead I used $(document).one(":passagedisplay", function(ev) { which also does the job.