A JavaScript syntax highlighter in 40 lines of JavaScript

A JavaScript syntax highlighter in 40 lines of JavaScript

About

A basic JavaScript syntax structure highlighter to be utilized when offer code in a html page. Exceptionally simple to add new standards or to change the saved words or techniques.

How to use it

Add the record syntax.min.js to the furthest limit of your code and it will naturally feature each <code> label that you have in your page. Or then again utilize the concede attribute:

let canvas, c, w, h, scale, game, tileSize, moves, status
const init = () => {
    moves = 0
    tileSize = 96
    createGame(3)
    canvas = document.createElement('canvas')
    updateCanvas()
    const options = document.createElement('div')
    const scrambleBtn = document.createElement('button')
    scrambleBtn.innerText = "Scramble"
    scrambleBtn.addEventListener('click', () => scramble(Math.floor(Math.random()*100)))
    const cubeSize = document.createElement('select')
    for(let i = 3; i < 6; i++){
        const opt = document.createElement('option')
        opt.value = i
        opt.innerText = i
        cubeSize.appendChild(opt)
    }
    cubeSize.addEventListener('change', function(){
        const value = parseInt(this.value)
        tileSize = 96 - (value-3) * 16
        createGame(value)
        updateCanvas()
        scramble(100)
    })
    status = document.createElement('div')
    status.id = 'status'
    options.appendChild(cubeSize)
    options.appendChild(scrambleBtn)
    document.body.appendChild(status)
    document.body.appendChild(canvas)
    document.body.appendChild(options)
    scramble(1000)
}
const updateCanvas = () => {
    scale = window.devicePixelRatio
    w = game[0].length * tileSize
    h =  game.length * tileSize
    canvas.width = w * scale
    canvas.height = h * scale
    canvas.style.width = `${w}px`
    canvas.style.height = `${h}px`
    c = canvas.getContext('2d')
    c.scale(scale, scale)
    c.textAlign = "center"
    c.textBaseline = "middle"
    const fontSize = 24
    c.font = `${fontSize}px Arial`
    canvas.addEventListener('click', move)
}
const createGame = num => {
    game = Array(num).fill().map(_ => Array(num).fill(0))
    for(let i = 0; i < num; i++){
        for(let j = 0; j < num; j++){
            game[i][j] = (i * num + j + 1) % (num*num)
        }
    }
}
const getPos = e => {return {x: Math.floor(e.offsetX / tileSize), y: Math.floor(e.offsetY / tileSize)}}
const draw = () => {
    c.clearRect(0,0,w,h)
    c.strokeStyle = "black"
    for(let i = 0; i < game.length; i++){
        for(let j = 0; j < game[0].length; j++){
            const piece = game[i][j]
            if(!piece)
                continue
            const x = j * tileSize
            const y = i * tileSize
            c.fillStyle = "white"
            c.fillRect(x, y, tileSize, tileSize)
            c.strokeRect(x, y, tileSize, tileSize)
            c.fillStyle = "black"
            c.fillText(piece, x+tileSize/2, y+tileSize/2)
        }
    }
}
const findEmptyPos = () => {
    for(let i = 0; i < game.length; i++){
        for(let j = 0; j < game[0].length; j++){
            if(!game[i][j]){
                return {x: j, y: i}
            }
        }
    }
}
const checkInvalid = (x, y, i, j) => 
    (y+i < 0 || y+i > game.length-1 || x+j < 0 || x+j > game[0].length-1 || i+j === 0 || i === j)
const getNeighbour = pos => {
    let n = []
    for(let i = -1; i < 2; i++){
        for(let j = -1; j < 2; j++){
            if(checkInvalid(pos.x, pos.y, i, j))
                continue
            if(game[pos.y+i][pos.x+j])
                n.push({x: pos.x+j, y: pos.y+i})
        }
    }
    return n
}
const scramble = (num = 1) => {
    moves = 0
    for(let i = 0; i < num; i++){
        const emptyPos = findEmptyPos()
        const n = getNeighbour(emptyPos)
        const move = n[Math.floor(Math.random() * n.length)]
        tradePos(move, emptyPos)
    }
    draw()
    if(!checkGameWin()){
        status.innerText = ''
    }
}
const lookEmptyPos = pos => {
    for(let i = -1; i < 2; i++){
        for(let j = -1; j < 2; j++){
            if(checkInvalid(pos.x, pos.y, i, j))
                continue
            if(!game[pos.y+i][pos.x+j])
                return {x: pos.x+j, y: pos.y+i}
        }
    }
    return null
}
const tradePos = (pos, newPos) => {
    if(!newPos) 
        return
    game[newPos.y][newPos.x] = game[pos.y][pos.x]
    game[pos.y][pos.x] = 0
}
const checkGameWin = () => {
    for(let i = 0; i < game.length * game[0].length - 1; i++){
        const x = i % game[0].length
        const y = Math.floor( i / game[0].length )
        if(game[y][x] != i+1)
            return false
    }
    return true
}
const move = e => {
    const pos = getPos(e)
    const newPos = lookEmptyPos(pos)
    tradePos(pos, newPos)
    status.innerText = checkGameWin() ? `Win - ${moves} moves` : ''
    draw()
    moves += 1
}
init()

The script will create <spam> tag for every reserved word, variable, methods and numbers so you can target them with CSS.

code {
  font-family: Consolas,"courier new";
  color: #EEE;
  background-color: #333;
  padding: 2px;
  font-size: 105%;
  display: block;
  white-space: pre;
  counter-reset: line;
}

code > div {
  counter-increment: line;
  display: block;
  min-height: 1em;
}

code > div::before {
  content: counter(line) 'A0';
  display: inline-block;
  width: 4ch;
  text-align: left;
  -webkit-select: none;
  color: #666;
}

.reserved {
  font-weight: bold;
  color: #55C;
}

.methods {
  font-weight: bold;
  color: green;
}

.variable {
  color: orange;
}

.comment {
  color: gray;
}

.number {
  color: red;
}

Fell free to share anything you like to more readily suit your requirements.