From 2d7cfd128aef8286077651e75fdadf55789c6143 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 9 Jan 2017 15:47:29 -0500 Subject: [PATCH 01/36] use a single global tempo owned by audioengine --- src/blocks/scratch3_sound.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index f1a16f17..34ca40a3 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -114,18 +114,18 @@ Scratch3SoundBlocks.prototype.getVolume = function (args, util) { return util.target.audioPlayer.currentVolume; }; -Scratch3SoundBlocks.prototype.setTempo = function (args, util) { +Scratch3SoundBlocks.prototype.setTempo = function (args) { var value = Cast.toNumber(args.TEMPO); - util.target.audioPlayer.setTempo(value); + this.runtime.audioEngine.setTempo(value); }; -Scratch3SoundBlocks.prototype.changeTempo = function (args, util) { +Scratch3SoundBlocks.prototype.changeTempo = function (args) { var value = Cast.toNumber(args.TEMPO); - util.target.audioPlayer.changeTempo(value); + this.runtime.audioEngine.changeTempo(value); }; -Scratch3SoundBlocks.prototype.getTempo = function (args, util) { - return util.target.audioPlayer.currentTempo; +Scratch3SoundBlocks.prototype.getTempo = function () { + return this.runtime.audioEngine.currentTempo; }; Scratch3SoundBlocks.prototype.soundsMenu = function (args) { From ce8b069140e60aa98f71b3126b41e73918290c7a Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 9 Jan 2017 15:48:02 -0500 Subject: [PATCH 02/36] include volume reporter --- src/blocks/scratch3_sound.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index 34ca40a3..c67108ff 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -30,6 +30,7 @@ Scratch3SoundBlocks.prototype.getPrimitives = function () { sound_effects_menu: this.effectsMenu, sound_setvolumeto: this.setVolume, sound_changevolumeby: this.changeVolume, + sound_volume: this.getVolume, sound_settempotobpm: this.setTempo, sound_changetempoby: this.changeTempo, sound_tempo: this.getTempo From 4d7ff97011288edc63bb519bcd29a491bd4109cd Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 9 Jan 2017 15:48:31 -0500 Subject: [PATCH 03/36] instrument number is zero-indexed inside audio engine --- src/blocks/scratch3_sound.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index c67108ff..8e177957 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -84,6 +84,7 @@ Scratch3SoundBlocks.prototype.restForBeats = function (args, util) { Scratch3SoundBlocks.prototype.setInstrument = function (args, util) { var instNum = Cast.toNumber(args.INSTRUMENT); + instNum -= 1; return util.target.audioPlayer.setInstrument(instNum); }; From 3344c92f7ee3e51948ebd96b900897a47d966a76 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 10 Jan 2017 18:00:02 -0500 Subject: [PATCH 04/36] =?UTF-8?q?change=20=E2=80=98drumtype=E2=80=99=20to?= =?UTF-8?q?=20=E2=80=98drum=E2=80=99=20in=20specmap?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/import/sb2specmap.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/import/sb2specmap.js b/src/import/sb2specmap.js index 204f3c54..06586186 100644 --- a/src/import/sb2specmap.js +++ b/src/import/sb2specmap.js @@ -408,7 +408,7 @@ var specMap = { { type: 'input', inputOp: 'math_number', - inputName: 'DRUMTYPE' + inputName: 'DRUM' }, { type: 'input', From 76a475c31cd52fa15ccbcd7550637ffd2aae64ff Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 10 Jan 2017 18:00:33 -0500 Subject: [PATCH 05/36] cast args to numbers --- src/blocks/scratch3_sound.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index 8e177957..c2a48135 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -71,15 +71,20 @@ Scratch3SoundBlocks.prototype.stopAllSounds = function (args, util) { }; Scratch3SoundBlocks.prototype.playNoteForBeats = function (args, util) { - return util.target.audioPlayer.playNoteForBeats(args.NOTE, args.BEATS); + var note = Cast.toNumber(args.NOTE); + var beats = Cast.toNumber(args.BEATS); + return util.target.audioPlayer.playNoteForBeats(note, beats); }; Scratch3SoundBlocks.prototype.playDrumForBeats = function (args, util) { - return util.target.audioPlayer.playDrumForBeats(args.DRUM, args.BEATS); + var drum = Cast.toNumber(args.DRUMTYPE); + var beats = Cast.toNumber(args.BEATS); + return util.target.audioPlayer.playDrumForBeats(drum, beats); }; Scratch3SoundBlocks.prototype.restForBeats = function (args, util) { - return util.target.audioPlayer.waitForBeats(args.BEATS); + var beats = Cast.toNumber(args.BEATS); + return util.target.audioPlayer.waitForBeats(beats); }; Scratch3SoundBlocks.prototype.setInstrument = function (args, util) { From 71339874de45b4ed92a77662cd152288408a7683 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 11 Jan 2017 11:22:58 -0500 Subject: [PATCH 06/36] fix name of DRUM arg --- src/blocks/scratch3_sound.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index c2a48135..a24e17bc 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -77,7 +77,7 @@ Scratch3SoundBlocks.prototype.playNoteForBeats = function (args, util) { }; Scratch3SoundBlocks.prototype.playDrumForBeats = function (args, util) { - var drum = Cast.toNumber(args.DRUMTYPE); + var drum = Cast.toNumber(args.DRUM); var beats = Cast.toNumber(args.BEATS); return util.target.audioPlayer.playDrumForBeats(drum, beats); }; From f2ad364b9001503ed436dc4cbe0964c7a8228861 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 11 Jan 2017 11:23:39 -0500 Subject: [PATCH 07/36] vm converts one-indexing of drums and instruments to zero-indexing --- src/blocks/scratch3_sound.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index a24e17bc..20b4e27a 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -78,6 +78,7 @@ Scratch3SoundBlocks.prototype.playNoteForBeats = function (args, util) { Scratch3SoundBlocks.prototype.playDrumForBeats = function (args, util) { var drum = Cast.toNumber(args.DRUM); + drum -= 1; // drums are one-indexed var beats = Cast.toNumber(args.BEATS); return util.target.audioPlayer.playDrumForBeats(drum, beats); }; @@ -89,7 +90,7 @@ Scratch3SoundBlocks.prototype.restForBeats = function (args, util) { Scratch3SoundBlocks.prototype.setInstrument = function (args, util) { var instNum = Cast.toNumber(args.INSTRUMENT); - instNum -= 1; + instNum -= 1; // instruments are one-indexed return util.target.audioPlayer.setInstrument(instNum); }; From 9cc1c316d65f697f23baab1a505f6a5880ce0f2a Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 11 Jan 2017 11:41:21 -0500 Subject: [PATCH 08/36] wrapclamp drum and instrument to num available --- src/blocks/scratch3_sound.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index 20b4e27a..87fbb0aa 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -79,6 +79,7 @@ Scratch3SoundBlocks.prototype.playNoteForBeats = function (args, util) { Scratch3SoundBlocks.prototype.playDrumForBeats = function (args, util) { var drum = Cast.toNumber(args.DRUM); drum -= 1; // drums are one-indexed + drum = MathUtil.wrapClamp(drum, 0, this.runtime.audioEngine.numDrums); var beats = Cast.toNumber(args.BEATS); return util.target.audioPlayer.playDrumForBeats(drum, beats); }; @@ -91,6 +92,7 @@ Scratch3SoundBlocks.prototype.restForBeats = function (args, util) { Scratch3SoundBlocks.prototype.setInstrument = function (args, util) { var instNum = Cast.toNumber(args.INSTRUMENT); instNum -= 1; // instruments are one-indexed + instNum = MathUtil.wrapClamp(instNum, 0, this.runtime.audioEngine.numInstruments); return util.target.audioPlayer.setInstrument(instNum); }; From 402270353750fe9b540219c636c5c63d14f6ecb0 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 11 Jan 2017 15:42:07 -0500 Subject: [PATCH 09/36] move audio state vars into rendered target --- src/sprites/rendered-target.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index b5b89365..d7a94fd0 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -46,6 +46,19 @@ var RenderedTarget = function (sprite, runtime) { brightness: 0, ghost: 0 }; + + /** + * Map of current audio effect values. + * @type {!Object.} + */ + this.audioEffects = { + pitch: 0, + pan: 0, + echo: 0, + reverb: 0, + fuzz: 0., + robot: 0 + }; }; util.inherits(RenderedTarget, Target); @@ -151,6 +164,18 @@ RenderedTarget.prototype.rotationStyle = ( RenderedTarget.ROTATION_STYLE_ALL_AROUND ); +/** + * Volume for playback of sounds, as a percentage + * @type {number} + */ +RenderedTarget.prototype.volume = 100; + +/** + * Currently selected instrument + * @type {number} + */ +RenderedTarget.prototype.currentInstrument = 0; + /** * Set the X and Y coordinates. * @param {!number} x New X coordinate, in Scratch coordinates. From a25e117412522b6b012eeb484ec03c787f97feab Mon Sep 17 00:00:00 2001 From: griffpatch Date: Sat, 28 Jan 2017 14:38:13 +0000 Subject: [PATCH 10/36] optimisation/avoid negative index lookups #407 Run-time Optimisation The thread.stackFrames array is accessed all the time to retrieve the 'parent' stack frame. This is done using as index of [this.stack.length - 1]. However, a lot of the time this evaluated to [-1]. Although this results in null, which is fine, to get to this javascript actually defers from a numeric array lookup to an object lookup using the string "-1". This is roughly 100 times slower to compute and so a simple catch for negative indexes is well worth the extra check. --- src/engine/thread.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/engine/thread.js b/src/engine/thread.js index 765f48b7..6402bd4e 100644 --- a/src/engine/thread.js +++ b/src/engine/thread.js @@ -101,7 +101,6 @@ Thread.prototype.pushStack = function (blockId) { if (this.stack.length > this.stackFrames.length) { // Copy warp mode from any higher level. var warpMode = false; - if (this.stackFrames[this.stackFrames.length - 1]) { warpMode = this.stackFrames[this.stackFrames.length - 1].warpMode; } this.stackFrames.push({ @@ -129,7 +128,7 @@ Thread.prototype.popStack = function () { * @return {?string} Block ID on top of stack. */ Thread.prototype.peekStack = function () { - return this.stack[this.stack.length - 1]; + return this.stack.length > 0 ? this.stack[this.stack.length - 1] : null; }; @@ -138,7 +137,7 @@ Thread.prototype.peekStack = function () { * @return {?Object} Last stack frame stored on this thread. */ Thread.prototype.peekStackFrame = function () { - return this.stackFrames[this.stackFrames.length - 1]; + return this.stackFrames.length > 0 ? this.stackFrames[this.stackFrames.length - 1] : null; }; /** @@ -146,7 +145,7 @@ Thread.prototype.peekStackFrame = function () { * @return {?Object} Second to last stack frame stored on this thread. */ Thread.prototype.peekParentStackFrame = function () { - return this.stackFrames[this.stackFrames.length - 2]; + return this.stackFrames.length > 1 ? this.stackFrames[this.stackFrames.length - 2] : null; }; /** From bd9159b9fb2624292d53a369c2f5802522567d32 Mon Sep 17 00:00:00 2001 From: griffpatch Date: Sat, 28 Jan 2017 14:39:22 +0000 Subject: [PATCH 11/36] More negative index checks --- src/engine/thread.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/engine/thread.js b/src/engine/thread.js index 6402bd4e..3fd92efa 100644 --- a/src/engine/thread.js +++ b/src/engine/thread.js @@ -101,6 +101,7 @@ Thread.prototype.pushStack = function (blockId) { if (this.stack.length > this.stackFrames.length) { // Copy warp mode from any higher level. var warpMode = false; + if (this.stackFrames.length > 0 && this.stackFrames[this.stackFrames.length - 1]) { warpMode = this.stackFrames[this.stackFrames.length - 1].warpMode; } this.stackFrames.push({ From 76c9c993f217c39caeb9abe787c15bf822949067 Mon Sep 17 00:00:00 2001 From: griffpatch Date: Sat, 28 Jan 2017 16:33:20 +0000 Subject: [PATCH 12/36] Optimisation - Remove redunant lookups It's surprising how much time it takes to do an object member lookup, so reducing these is a great idea. --- src/engine/blocks.js | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index 84d09a6f..d2215a2c 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -54,8 +54,8 @@ Blocks.prototype.getScripts = function () { * @return {?string} ID of next block in the sequence */ Blocks.prototype.getNextBlock = function (id) { - if (typeof this._blocks[id] === 'undefined') return null; - return this._blocks[id].next; + var block = this._blocks[id]; + return (typeof block === 'undefined') ? null : block.next; }; /** @@ -85,8 +85,8 @@ Blocks.prototype.getBranch = function (id, branchNum) { * @return {?string} the opcode corresponding to that block */ Blocks.prototype.getOpcode = function (id) { - if (typeof this._blocks[id] === 'undefined') return null; - return this._blocks[id].opcode; + var block = this._blocks[id]; + return (typeof block === 'undefined') ? null : block.opcode; }; /** @@ -95,8 +95,8 @@ Blocks.prototype.getOpcode = function (id) { * @return {!Object} All fields and their values. */ Blocks.prototype.getFields = function (id) { - if (typeof this._blocks[id] === 'undefined') return null; - return this._blocks[id].fields; + var block = this._blocks[id]; + return (typeof block === 'undefined') ? null : block.fields; }; /** @@ -105,13 +105,14 @@ Blocks.prototype.getFields = function (id) { * @return {!Object} All non-branch inputs and their associated blocks. */ Blocks.prototype.getInputs = function (id) { - if (typeof this._blocks[id] === 'undefined') return null; + var block = this._blocks[id]; + if (typeof block === 'undefined') return null; var inputs = {}; - for (var input in this._blocks[id].inputs) { + for (var input in block.inputs) { // Ignore blocks prefixed with branch prefix. if (input.substring(0, Blocks.BRANCH_INPUT_PREFIX.length) !== Blocks.BRANCH_INPUT_PREFIX) { - inputs[input] = this._blocks[id].inputs[input]; + inputs[input] = block.inputs[input]; } } return inputs; @@ -123,8 +124,8 @@ Blocks.prototype.getInputs = function (id) { * @return {!Object} Mutation for the block. */ Blocks.prototype.getMutation = function (id) { - if (typeof this._blocks[id] === 'undefined') return null; - return this._blocks[id].mutation; + var block = this._blocks[id]; + return (typeof block === 'undefined') ? null : block.mutation; }; /** @@ -133,8 +134,8 @@ Blocks.prototype.getMutation = function (id) { * @return {?string} ID of top-level script block. */ Blocks.prototype.getTopLevelScript = function (id) { - if (typeof this._blocks[id] === 'undefined') return null; var block = this._blocks[id]; + if (typeof block === 'undefined') return null; while (block.parent !== null) { block = this._blocks[block.parent]; } @@ -271,14 +272,15 @@ Blocks.prototype.createBlock = function (block) { Blocks.prototype.changeBlock = function (args) { // Validate if (args.element !== 'field' && args.element !== 'mutation') return; - if (typeof this._blocks[args.id] === 'undefined') return; + var block = this._blocks[args.id]; + if (typeof block === 'undefined') return; if (args.element === 'field') { // Update block value - if (!this._blocks[args.id].fields[args.name]) return; - this._blocks[args.id].fields[args.name].value = args.value; + if (!block.fields[args.name]) return; + block.fields[args.name].value = args.value; } else if (args.element === 'mutation') { - this._blocks[args.id].mutation = mutationAdapter(args.value); + block.mutation = mutationAdapter(args.value); } }; From fbf2c0c34527261e31da910d61b15aaef9fc3b41 Mon Sep 17 00:00:00 2001 From: griffpatch Date: Sat, 28 Jan 2017 17:06:07 +0000 Subject: [PATCH 13/36] Optimisation - Recycle Stack Frame for cosecutive blocks This saves popping, destroying, recreating, and pushing the stack frame, and then reassigning the warp mode attribute for every block to block step in the execution. --- src/engine/thread.js | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/engine/thread.js b/src/engine/thread.js index 765f48b7..452d7236 100644 --- a/src/engine/thread.js +++ b/src/engine/thread.js @@ -115,6 +115,22 @@ Thread.prototype.pushStack = function (blockId) { } }; +/** + * Reset the stack frame for use by the next block. + * (avoids popping and re-pushing a new stack frame - keeps the warpmode the same + * @param {string} blockId Block ID to push to stack. + */ +Thread.prototype.reuseStackForNextBlock = function (blockId) { + this.stack[this.stack.length - 1] = blockId; + var frame = this.stackFrames[this.stackFrames.length - 1]; + frame.isLoop = false; + // frame.warpMode = warpMode; // warp mode stays the same when reusing the stack frame. + frame.reported = {}; + frame.waitingReporter = null; + frame.params = {}; + frame.executionContext = {}; +}; + /** * Pop last block on the stack and its stack frame. * @return {string} Block ID popped from the stack. @@ -203,15 +219,7 @@ Thread.prototype.atStackTop = function () { */ Thread.prototype.goToNextBlock = function () { var nextBlockId = this.target.blocks.getNextBlock(this.peekStack()); - // Copy warp mode to next block. - var warpMode = this.peekStackFrame().warpMode; - // The current block is on the stack - pop it and push the next. - // Note that this could push `null` - that is handled by the sequencer. - this.popStack(); - this.pushStack(nextBlockId); - if (this.peekStackFrame()) { - this.peekStackFrame().warpMode = warpMode; - } + this.reuseStackForNextBlock(nextBlockId); }; /** From d5e0d433aad5547f2dd99bc1c9b68b3dab2bc391 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 30 Jan 2017 10:43:28 -0500 Subject: [PATCH 14/36] include md5 in sprite sound object --- src/import/sb2import.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/import/sb2import.js b/src/import/sb2import.js index 92600ed6..c00f2b5e 100644 --- a/src/import/sb2import.js +++ b/src/import/sb2import.js @@ -61,7 +61,8 @@ var parseScratchObject = function (object, runtime, topLevel) { rate: sound.rate, sampleCount: sound.sampleCount, soundID: sound.soundID, - name: sound.soundName + name: sound.soundName, + md5: sound.md5 }); } } From 4e57ab561d0672e5fc16bc2b7463a75fd2d49ed0 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 30 Jan 2017 10:45:15 -0500 Subject: [PATCH 15/36] move playsound function to target --- src/blocks/scratch3_sound.js | 4 ++-- src/sprites/rendered-target.js | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index 87fbb0aa..4bdfd2da 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -39,12 +39,12 @@ Scratch3SoundBlocks.prototype.getPrimitives = function () { Scratch3SoundBlocks.prototype.playSound = function (args, util) { var index = this._getSoundIndex(args.SOUND_MENU, util); - util.target.audioPlayer.playSound(index); + util.target.playSound(index); }; Scratch3SoundBlocks.prototype.playSoundAndWait = function (args, util) { var index = this._getSoundIndex(args.SOUND_MENU, util); - return util.target.audioPlayer.playSound(index); + return util.target.playSound(index); }; Scratch3SoundBlocks.prototype._getSoundIndex = function (soundName, util) { diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index d7a94fd0..3eab6142 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -465,6 +465,9 @@ RenderedTarget.prototype.updateAllDrawableProperties = function () { this.runtime.spriteInfoReport(this); }; +RenderedTarget.prototype.playSound = function (index) { + return this.audioPlayer.playSound(this.sprite.sounds[index].md5); +}; /** * Return the human-readable name for this rendered target, e.g., the sprite's name. * @override From 01e237a2d5ea954516a02e26269ebd1da436540a Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 30 Jan 2017 10:50:28 -0500 Subject: [PATCH 16/36] audio engine loads all sounds --- src/sprites/rendered-target.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index 3eab6142..25f3eeab 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -82,10 +82,9 @@ RenderedTarget.prototype.initDrawable = function () { this.audioPlayer = null; if (this.runtime && this.runtime.audioEngine) { if (this.isOriginal) { - this.sprite.audioPlayer = this.runtime.audioEngine.createPlayer(); - this.sprite.audioPlayer.loadSounds(this.sprite.sounds); + this.runtime.audioEngine.loadSounds(this.sprite.sounds); } - this.audioPlayer = this.sprite.audioPlayer; + this.audioPlayer = this.runtime.audioEngine.createPlayer(); } }; From 4e4b0b86e19d001fdeced12aca48de53f9d9b4a4 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 30 Jan 2017 10:53:12 -0500 Subject: [PATCH 17/36] target manages audio effect values --- src/blocks/scratch3_sound.js | 8 ++++++-- src/sprites/rendered-target.js | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index 4bdfd2da..4c1350ee 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -97,13 +97,17 @@ Scratch3SoundBlocks.prototype.setInstrument = function (args, util) { }; Scratch3SoundBlocks.prototype.setEffect = function (args, util) { + var effect = Cast.toString(args.EFFECT).toLowerCase(); var value = Cast.toNumber(args.VALUE); - util.target.audioPlayer.setEffect(args.EFFECT, value); + util.target.setAudioEffect(effect, value); }; Scratch3SoundBlocks.prototype.changeEffect = function (args, util) { + var effect = Cast.toString(args.EFFECT).toLowerCase(); var value = Cast.toNumber(args.VALUE); - util.target.audioPlayer.changeEffect(args.EFFECT, value); + if (!util.target.audioEffects.hasOwnProperty(effect)) return; + var newValue = value + util.target.audioEffects[effect]; + util.target.setAudioEffect(effect, newValue); }; Scratch3SoundBlocks.prototype.clearEffects = function (args, util) { diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index 25f3eeab..e31616e4 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -341,6 +341,23 @@ RenderedTarget.prototype.clearEffects = function () { } }; +/** + * Set a particular audio effect value. + * @param {!string} effectName Name audio of effect (see `RenderedTarget.prototype.audioEffects`). + * @param {!number} value Numerical magnitude of effect. + */ +RenderedTarget.prototype.setAudioEffect = function (effectName, value) { + if (!this.audioEffects.hasOwnProperty(effectName)) return; + this.audioEffects[effectName] = value; + this.audioPlayer.setEffect(effectName, value); +}; + +RenderedTarget.prototype.clearAudioEffects = function () { + for (var effectName in this.audioEffects) { + this.audioEffects[effectName] = 0; + this.audioPlayer.setEffect(effectName, 0); + } +}; /** * Set the current costume. * @param {number} index New index of costume. From 5966a46bbd4f9008e2c99ae2d97ea900abb1437a Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 30 Jan 2017 10:54:24 -0500 Subject: [PATCH 18/36] target manages instrument setting --- src/blocks/scratch3_sound.js | 3 ++- src/sprites/rendered-target.js | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index 4c1350ee..e47a688a 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -93,7 +93,8 @@ Scratch3SoundBlocks.prototype.setInstrument = function (args, util) { var instNum = Cast.toNumber(args.INSTRUMENT); instNum -= 1; // instruments are one-indexed instNum = MathUtil.wrapClamp(instNum, 0, this.runtime.audioEngine.numInstruments); - return util.target.audioPlayer.setInstrument(instNum); + util.target.setInstrument(instNum); + return this.runtime.audioEngine.instrumentPlayer.loadInstrument(instNum); }; Scratch3SoundBlocks.prototype.setEffect = function (args, util) { diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index e31616e4..714d11dc 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -484,6 +484,10 @@ RenderedTarget.prototype.updateAllDrawableProperties = function () { RenderedTarget.prototype.playSound = function (index) { return this.audioPlayer.playSound(this.sprite.sounds[index].md5); }; + +RenderedTarget.prototype.setInstrument = function (num) { + this.currentInstrument = num; +}; /** * Return the human-readable name for this rendered target, e.g., the sprite's name. * @override From cb4209b27a82797257eaaac9cb312d3f9b3dd7c4 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 30 Jan 2017 10:56:06 -0500 Subject: [PATCH 19/36] target handles playnote via audio engine --- src/blocks/scratch3_sound.js | 2 +- src/sprites/rendered-target.js | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index e47a688a..bb9c4daa 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -73,7 +73,7 @@ Scratch3SoundBlocks.prototype.stopAllSounds = function (args, util) { Scratch3SoundBlocks.prototype.playNoteForBeats = function (args, util) { var note = Cast.toNumber(args.NOTE); var beats = Cast.toNumber(args.BEATS); - return util.target.audioPlayer.playNoteForBeats(note, beats); + return util.target.playNoteForBeats(note, beats); }; Scratch3SoundBlocks.prototype.playDrumForBeats = function (args, util) { diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index 714d11dc..4464addd 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -488,6 +488,11 @@ RenderedTarget.prototype.playSound = function (index) { RenderedTarget.prototype.setInstrument = function (num) { this.currentInstrument = num; }; + +RenderedTarget.prototype.playNoteForBeats = function (note, beats) { + return this.runtime.audioEngine.playNoteForBeatsWithInst(note, beats, this.currentInstrument); +}; + /** * Return the human-readable name for this rendered target, e.g., the sprite's name. * @override From 9d8819ddc712e299a1dfc9a93c952dabf8fa953e Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 30 Jan 2017 10:56:31 -0500 Subject: [PATCH 20/36] audio engine handles rest block --- src/blocks/scratch3_sound.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index bb9c4daa..17ed63af 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -84,9 +84,9 @@ Scratch3SoundBlocks.prototype.playDrumForBeats = function (args, util) { return util.target.audioPlayer.playDrumForBeats(drum, beats); }; -Scratch3SoundBlocks.prototype.restForBeats = function (args, util) { +Scratch3SoundBlocks.prototype.restForBeats = function (args) { var beats = Cast.toNumber(args.BEATS); - return util.target.audioPlayer.waitForBeats(beats); + return this.runtime.audioEngine.waitForBeats(beats); }; Scratch3SoundBlocks.prototype.setInstrument = function (args, util) { From dd30e070526e99430bc4f09535b3146f9b7b15cf Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 30 Jan 2017 10:56:50 -0500 Subject: [PATCH 21/36] comments on getSoundIndex --- src/blocks/scratch3_sound.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index 17ed63af..f564ccba 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -48,17 +48,22 @@ Scratch3SoundBlocks.prototype.playSoundAndWait = function (args, util) { }; Scratch3SoundBlocks.prototype._getSoundIndex = function (soundName, util) { + // if the sprite has no sounds, return 0 if (util.target.sprite.sounds.length === 0) { return 0; } var index; + // if the sound name is a number, wrapclamp it to a sound index if (Number(soundName)) { soundName = Number(soundName); var len = util.target.sprite.sounds.length; index = MathUtil.wrapClamp(soundName, 1, len) - 1; } else { + // else get the index for that sound of that name index = util.target.getSoundIndexByName(soundName); + // use zero if there is no sound by that name + // to match scratch 2.0 behavior, we should instead play no sound in this case if (index === -1) { index = 0; } From f7a3a8dbbe3140d1f52e73fcd21bfe8dbd533219 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 30 Jan 2017 11:40:20 -0500 Subject: [PATCH 22/36] merge --- dist.js | 19406 ++++++++++++++++++++++++++++++++++++++++++++ playground/vm.js | 19423 +++++++++++++++++++++++++++++++++++++++++++++ vm.js | 19412 ++++++++++++++++++++++++++++++++++++++++++++ vm.min.js | 13 + 4 files changed, 58254 insertions(+) create mode 100644 dist.js create mode 100644 playground/vm.js create mode 100644 vm.js create mode 100644 vm.min.js diff --git a/dist.js b/dist.js new file mode 100644 index 00000000..084738ec --- /dev/null +++ b/dist.js @@ -0,0 +1,19406 @@ +module.exports = +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; + +/******/ // The require function +/******/ function __webpack_require__(moduleId) { + +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; + +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; + +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + +/******/ // Flag the module as loaded +/******/ module.loaded = true; + +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } + + +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; + +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; + +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; + +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ function(module, exports, __webpack_require__) { + + var VirtualMachine = __webpack_require__(102); + + module.exports = VirtualMachine; + + +/***/ }, +/* 1 */ +/***/ function(module, exports) { + + // Copyright Joyent, Inc. and other Node contributors. + // + // Permission is hereby granted, free of charge, to any person obtaining a + // copy of this software and associated documentation files (the + // "Software"), to deal in the Software without restriction, including + // without limitation the rights to use, copy, modify, merge, publish, + // distribute, sublicense, and/or sell copies of the Software, and to permit + // persons to whom the Software is furnished to do so, subject to the + // following conditions: + // + // The above copyright notice and this permission notice shall be included + // in all copies or substantial portions of the Software. + // + // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + // USE OR OTHER DEALINGS IN THE SOFTWARE. + + function EventEmitter() { + this._events = this._events || {}; + this._maxListeners = this._maxListeners || undefined; + } + module.exports = EventEmitter; + + // Backwards-compat with node 0.10.x + EventEmitter.EventEmitter = EventEmitter; + + EventEmitter.prototype._events = undefined; + EventEmitter.prototype._maxListeners = undefined; + + // By default EventEmitters will print a warning if more than 10 listeners are + // added to it. This is a useful default which helps finding memory leaks. + EventEmitter.defaultMaxListeners = 10; + + // Obviously not all Emitters should be limited to 10. This function allows + // that to be increased. Set to zero for unlimited. + EventEmitter.prototype.setMaxListeners = function(n) { + if (!isNumber(n) || n < 0 || isNaN(n)) + throw TypeError('n must be a positive number'); + this._maxListeners = n; + return this; + }; + + EventEmitter.prototype.emit = function(type) { + var er, handler, len, args, i, listeners; + + if (!this._events) + this._events = {}; + + // If there is no 'error' event listener then throw. + if (type === 'error') { + if (!this._events.error || + (isObject(this._events.error) && !this._events.error.length)) { + er = arguments[1]; + if (er instanceof Error) { + throw er; // Unhandled 'error' event + } else { + // At least give some kind of context to the user + var err = new Error('Uncaught, unspecified "error" event. (' + er + ')'); + err.context = er; + throw err; + } + } + } + + handler = this._events[type]; + + if (isUndefined(handler)) + return false; + + if (isFunction(handler)) { + switch (arguments.length) { + // fast cases + case 1: + handler.call(this); + break; + case 2: + handler.call(this, arguments[1]); + break; + case 3: + handler.call(this, arguments[1], arguments[2]); + break; + // slower + default: + args = Array.prototype.slice.call(arguments, 1); + handler.apply(this, args); + } + } else if (isObject(handler)) { + args = Array.prototype.slice.call(arguments, 1); + listeners = handler.slice(); + len = listeners.length; + for (i = 0; i < len; i++) + listeners[i].apply(this, args); + } + + return true; + }; + + EventEmitter.prototype.addListener = function(type, listener) { + var m; + + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + if (!this._events) + this._events = {}; + + // To avoid recursion in the case that type === "newListener"! Before + // adding it to the listeners, first emit "newListener". + if (this._events.newListener) + this.emit('newListener', type, + isFunction(listener.listener) ? + listener.listener : listener); + + if (!this._events[type]) + // Optimize the case of one listener. Don't need the extra array object. + this._events[type] = listener; + else if (isObject(this._events[type])) + // If we've already got an array, just append. + this._events[type].push(listener); + else + // Adding the second element, need to change to array. + this._events[type] = [this._events[type], listener]; + + // Check for listener leak + if (isObject(this._events[type]) && !this._events[type].warned) { + if (!isUndefined(this._maxListeners)) { + m = this._maxListeners; + } else { + m = EventEmitter.defaultMaxListeners; + } + + if (m && m > 0 && this._events[type].length > m) { + this._events[type].warned = true; + console.error('(node) warning: possible EventEmitter memory ' + + 'leak detected. %d listeners added. ' + + 'Use emitter.setMaxListeners() to increase limit.', + this._events[type].length); + if (typeof console.trace === 'function') { + // not supported in IE 10 + console.trace(); + } + } + } + + return this; + }; + + EventEmitter.prototype.on = EventEmitter.prototype.addListener; + + EventEmitter.prototype.once = function(type, listener) { + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + var fired = false; + + function g() { + this.removeListener(type, g); + + if (!fired) { + fired = true; + listener.apply(this, arguments); + } + } + + g.listener = listener; + this.on(type, g); + + return this; + }; + + // emits a 'removeListener' event iff the listener was removed + EventEmitter.prototype.removeListener = function(type, listener) { + var list, position, length, i; + + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + if (!this._events || !this._events[type]) + return this; + + list = this._events[type]; + length = list.length; + position = -1; + + if (list === listener || + (isFunction(list.listener) && list.listener === listener)) { + delete this._events[type]; + if (this._events.removeListener) + this.emit('removeListener', type, listener); + + } else if (isObject(list)) { + for (i = length; i-- > 0;) { + if (list[i] === listener || + (list[i].listener && list[i].listener === listener)) { + position = i; + break; + } + } + + if (position < 0) + return this; + + if (list.length === 1) { + list.length = 0; + delete this._events[type]; + } else { + list.splice(position, 1); + } + + if (this._events.removeListener) + this.emit('removeListener', type, listener); + } + + return this; + }; + + EventEmitter.prototype.removeAllListeners = function(type) { + var key, listeners; + + if (!this._events) + return this; + + // not listening for removeListener, no need to emit + if (!this._events.removeListener) { + if (arguments.length === 0) + this._events = {}; + else if (this._events[type]) + delete this._events[type]; + return this; + } + + // emit removeListener for all listeners on all events + if (arguments.length === 0) { + for (key in this._events) { + if (key === 'removeListener') continue; + this.removeAllListeners(key); + } + this.removeAllListeners('removeListener'); + this._events = {}; + return this; + } + + listeners = this._events[type]; + + if (isFunction(listeners)) { + this.removeListener(type, listeners); + } else if (listeners) { + // LIFO order + while (listeners.length) + this.removeListener(type, listeners[listeners.length - 1]); + } + delete this._events[type]; + + return this; + }; + + EventEmitter.prototype.listeners = function(type) { + var ret; + if (!this._events || !this._events[type]) + ret = []; + else if (isFunction(this._events[type])) + ret = [this._events[type]]; + else + ret = this._events[type].slice(); + return ret; + }; + + EventEmitter.prototype.listenerCount = function(type) { + if (this._events) { + var evlistener = this._events[type]; + + if (isFunction(evlistener)) + return 1; + else if (evlistener) + return evlistener.length; + } + return 0; + }; + + EventEmitter.listenerCount = function(emitter, type) { + return emitter.listenerCount(type); + }; + + function isFunction(arg) { + return typeof arg === 'function'; + } + + function isNumber(arg) { + return typeof arg === 'number'; + } + + function isObject(arg) { + return typeof arg === 'object' && arg !== null; + } + + function isUndefined(arg) { + return arg === void 0; + } + + +/***/ }, +/* 2 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global, process) {// Copyright Joyent, Inc. and other Node contributors. + // + // Permission is hereby granted, free of charge, to any person obtaining a + // copy of this software and associated documentation files (the + // "Software"), to deal in the Software without restriction, including + // without limitation the rights to use, copy, modify, merge, publish, + // distribute, sublicense, and/or sell copies of the Software, and to permit + // persons to whom the Software is furnished to do so, subject to the + // following conditions: + // + // The above copyright notice and this permission notice shall be included + // in all copies or substantial portions of the Software. + // + // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + // USE OR OTHER DEALINGS IN THE SOFTWARE. + + var formatRegExp = /%[sdj%]/g; + exports.format = function(f) { + if (!isString(f)) { + var objects = []; + for (var i = 0; i < arguments.length; i++) { + objects.push(inspect(arguments[i])); + } + return objects.join(' '); + } + + var i = 1; + var args = arguments; + var len = args.length; + var str = String(f).replace(formatRegExp, function(x) { + if (x === '%%') return '%'; + if (i >= len) return x; + switch (x) { + case '%s': return String(args[i++]); + case '%d': return Number(args[i++]); + case '%j': + try { + return JSON.stringify(args[i++]); + } catch (_) { + return '[Circular]'; + } + default: + return x; + } + }); + for (var x = args[i]; i < len; x = args[++i]) { + if (isNull(x) || !isObject(x)) { + str += ' ' + x; + } else { + str += ' ' + inspect(x); + } + } + return str; + }; + + + // Mark that a method should not be used. + // Returns a modified function which warns once by default. + // If --no-deprecation is set, then it is a no-op. + exports.deprecate = function(fn, msg) { + // Allow for deprecating things in the process of starting up. + if (isUndefined(global.process)) { + return function() { + return exports.deprecate(fn, msg).apply(this, arguments); + }; + } + + if (process.noDeprecation === true) { + return fn; + } + + var warned = false; + function deprecated() { + if (!warned) { + if (process.throwDeprecation) { + throw new Error(msg); + } else if (process.traceDeprecation) { + console.trace(msg); + } else { + console.error(msg); + } + warned = true; + } + return fn.apply(this, arguments); + } + + return deprecated; + }; + + + var debugs = {}; + var debugEnviron; + exports.debuglog = function(set) { + if (isUndefined(debugEnviron)) + debugEnviron = process.env.NODE_DEBUG || ''; + set = set.toUpperCase(); + if (!debugs[set]) { + if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { + var pid = process.pid; + debugs[set] = function() { + var msg = exports.format.apply(exports, arguments); + console.error('%s %d: %s', set, pid, msg); + }; + } else { + debugs[set] = function() {}; + } + } + return debugs[set]; + }; + + + /** + * Echos the value of a value. Trys to print the value out + * in the best way possible given the different types. + * + * @param {Object} obj The object to print out. + * @param {Object} opts Optional options object that alters the output. + */ + /* legacy: obj, showHidden, depth, colors*/ + function inspect(obj, opts) { + // default options + var ctx = { + seen: [], + stylize: stylizeNoColor + }; + // legacy... + if (arguments.length >= 3) ctx.depth = arguments[2]; + if (arguments.length >= 4) ctx.colors = arguments[3]; + if (isBoolean(opts)) { + // legacy... + ctx.showHidden = opts; + } else if (opts) { + // got an "options" object + exports._extend(ctx, opts); + } + // set default options + if (isUndefined(ctx.showHidden)) ctx.showHidden = false; + if (isUndefined(ctx.depth)) ctx.depth = 2; + if (isUndefined(ctx.colors)) ctx.colors = false; + if (isUndefined(ctx.customInspect)) ctx.customInspect = true; + if (ctx.colors) ctx.stylize = stylizeWithColor; + return formatValue(ctx, obj, ctx.depth); + } + exports.inspect = inspect; + + + // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics + inspect.colors = { + 'bold' : [1, 22], + 'italic' : [3, 23], + 'underline' : [4, 24], + 'inverse' : [7, 27], + 'white' : [37, 39], + 'grey' : [90, 39], + 'black' : [30, 39], + 'blue' : [34, 39], + 'cyan' : [36, 39], + 'green' : [32, 39], + 'magenta' : [35, 39], + 'red' : [31, 39], + 'yellow' : [33, 39] + }; + + // Don't use 'blue' not visible on cmd.exe + inspect.styles = { + 'special': 'cyan', + 'number': 'yellow', + 'boolean': 'yellow', + 'undefined': 'grey', + 'null': 'bold', + 'string': 'green', + 'date': 'magenta', + // "name": intentionally not styling + 'regexp': 'red' + }; + + + function stylizeWithColor(str, styleType) { + var style = inspect.styles[styleType]; + + if (style) { + return '\u001b[' + inspect.colors[style][0] + 'm' + str + + '\u001b[' + inspect.colors[style][1] + 'm'; + } else { + return str; + } + } + + + function stylizeNoColor(str, styleType) { + return str; + } + + + function arrayToHash(array) { + var hash = {}; + + array.forEach(function(val, idx) { + hash[val] = true; + }); + + return hash; + } + + + function formatValue(ctx, value, recurseTimes) { + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it + if (ctx.customInspect && + value && + isFunction(value.inspect) && + // Filter out the util module, it's inspect function is special + value.inspect !== exports.inspect && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value)) { + var ret = value.inspect(recurseTimes, ctx); + if (!isString(ret)) { + ret = formatValue(ctx, ret, recurseTimes); + } + return ret; + } + + // Primitive types cannot have properties + var primitive = formatPrimitive(ctx, value); + if (primitive) { + return primitive; + } + + // Look up the keys of the object. + var keys = Object.keys(value); + var visibleKeys = arrayToHash(keys); + + if (ctx.showHidden) { + keys = Object.getOwnPropertyNames(value); + } + + // IE doesn't make error fields non-enumerable + // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx + if (isError(value) + && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { + return formatError(value); + } + + // Some type of object without properties can be shortcutted. + if (keys.length === 0) { + if (isFunction(value)) { + var name = value.name ? ': ' + value.name : ''; + return ctx.stylize('[Function' + name + ']', 'special'); + } + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } + if (isDate(value)) { + return ctx.stylize(Date.prototype.toString.call(value), 'date'); + } + if (isError(value)) { + return formatError(value); + } + } + + var base = '', array = false, braces = ['{', '}']; + + // Make Array say that they are Array + if (isArray(value)) { + array = true; + braces = ['[', ']']; + } + + // Make functions say that they are functions + if (isFunction(value)) { + var n = value.name ? ': ' + value.name : ''; + base = ' [Function' + n + ']'; + } + + // Make RegExps say that they are RegExps + if (isRegExp(value)) { + base = ' ' + RegExp.prototype.toString.call(value); + } + + // Make dates with properties first say the date + if (isDate(value)) { + base = ' ' + Date.prototype.toUTCString.call(value); + } + + // Make error with message first say the error + if (isError(value)) { + base = ' ' + formatError(value); + } + + if (keys.length === 0 && (!array || value.length == 0)) { + return braces[0] + base + braces[1]; + } + + if (recurseTimes < 0) { + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } else { + return ctx.stylize('[Object]', 'special'); + } + } + + ctx.seen.push(value); + + var output; + if (array) { + output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); + } else { + output = keys.map(function(key) { + return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); + }); + } + + ctx.seen.pop(); + + return reduceToSingleString(output, base, braces); + } + + + function formatPrimitive(ctx, value) { + if (isUndefined(value)) + return ctx.stylize('undefined', 'undefined'); + if (isString(value)) { + var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') + .replace(/'/g, "\\'") + .replace(/\\"/g, '"') + '\''; + return ctx.stylize(simple, 'string'); + } + if (isNumber(value)) + return ctx.stylize('' + value, 'number'); + if (isBoolean(value)) + return ctx.stylize('' + value, 'boolean'); + // For some reason typeof null is "object", so special case here. + if (isNull(value)) + return ctx.stylize('null', 'null'); + } + + + function formatError(value) { + return '[' + Error.prototype.toString.call(value) + ']'; + } + + + function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { + var output = []; + for (var i = 0, l = value.length; i < l; ++i) { + if (hasOwnProperty(value, String(i))) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + String(i), true)); + } else { + output.push(''); + } + } + keys.forEach(function(key) { + if (!key.match(/^\d+$/)) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + key, true)); + } + }); + return output; + } + + + function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { + var name, str, desc; + desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; + if (desc.get) { + if (desc.set) { + str = ctx.stylize('[Getter/Setter]', 'special'); + } else { + str = ctx.stylize('[Getter]', 'special'); + } + } else { + if (desc.set) { + str = ctx.stylize('[Setter]', 'special'); + } + } + if (!hasOwnProperty(visibleKeys, key)) { + name = '[' + key + ']'; + } + if (!str) { + if (ctx.seen.indexOf(desc.value) < 0) { + if (isNull(recurseTimes)) { + str = formatValue(ctx, desc.value, null); + } else { + str = formatValue(ctx, desc.value, recurseTimes - 1); + } + if (str.indexOf('\n') > -1) { + if (array) { + str = str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n').substr(2); + } else { + str = '\n' + str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n'); + } + } + } else { + str = ctx.stylize('[Circular]', 'special'); + } + } + if (isUndefined(name)) { + if (array && key.match(/^\d+$/)) { + return str; + } + name = JSON.stringify('' + key); + if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + name = name.substr(1, name.length - 2); + name = ctx.stylize(name, 'name'); + } else { + name = name.replace(/'/g, "\\'") + .replace(/\\"/g, '"') + .replace(/(^"|"$)/g, "'"); + name = ctx.stylize(name, 'string'); + } + } + + return name + ': ' + str; + } + + + function reduceToSingleString(output, base, braces) { + var numLinesEst = 0; + var length = output.reduce(function(prev, cur) { + numLinesEst++; + if (cur.indexOf('\n') >= 0) numLinesEst++; + return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; + }, 0); + + if (length > 60) { + return braces[0] + + (base === '' ? '' : base + '\n ') + + ' ' + + output.join(',\n ') + + ' ' + + braces[1]; + } + + return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; + } + + + // NOTE: These type checking functions intentionally don't use `instanceof` + // because it is fragile and can be easily faked with `Object.create()`. + function isArray(ar) { + return Array.isArray(ar); + } + exports.isArray = isArray; + + function isBoolean(arg) { + return typeof arg === 'boolean'; + } + exports.isBoolean = isBoolean; + + function isNull(arg) { + return arg === null; + } + exports.isNull = isNull; + + function isNullOrUndefined(arg) { + return arg == null; + } + exports.isNullOrUndefined = isNullOrUndefined; + + function isNumber(arg) { + return typeof arg === 'number'; + } + exports.isNumber = isNumber; + + function isString(arg) { + return typeof arg === 'string'; + } + exports.isString = isString; + + function isSymbol(arg) { + return typeof arg === 'symbol'; + } + exports.isSymbol = isSymbol; + + function isUndefined(arg) { + return arg === void 0; + } + exports.isUndefined = isUndefined; + + function isRegExp(re) { + return isObject(re) && objectToString(re) === '[object RegExp]'; + } + exports.isRegExp = isRegExp; + + function isObject(arg) { + return typeof arg === 'object' && arg !== null; + } + exports.isObject = isObject; + + function isDate(d) { + return isObject(d) && objectToString(d) === '[object Date]'; + } + exports.isDate = isDate; + + function isError(e) { + return isObject(e) && + (objectToString(e) === '[object Error]' || e instanceof Error); + } + exports.isError = isError; + + function isFunction(arg) { + return typeof arg === 'function'; + } + exports.isFunction = isFunction; + + function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; + } + exports.isPrimitive = isPrimitive; + + exports.isBuffer = __webpack_require__(4); + + function objectToString(o) { + return Object.prototype.toString.call(o); + } + + + function pad(n) { + return n < 10 ? '0' + n.toString(10) : n.toString(10); + } + + + var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', + 'Oct', 'Nov', 'Dec']; + + // 26 Feb 16:19:34 + function timestamp() { + var d = new Date(); + var time = [pad(d.getHours()), + pad(d.getMinutes()), + pad(d.getSeconds())].join(':'); + return [d.getDate(), months[d.getMonth()], time].join(' '); + } + + + // log is just a thin wrapper to console.log that prepends a timestamp + exports.log = function() { + console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); + }; + + + /** + * Inherit the prototype methods from one constructor into another. + * + * The Function.prototype.inherits from lang.js rewritten as a standalone + * function (not on Function.prototype). NOTE: If this file is to be loaded + * during bootstrapping this function needs to be rewritten using some native + * functions as prototype setup using normal JavaScript does not work as + * expected during bootstrapping (see mirror.js in r114903). + * + * @param {function} ctor Constructor function which needs to inherit the + * prototype. + * @param {function} superCtor Constructor function to inherit prototype from. + */ + exports.inherits = __webpack_require__(5); + + exports._extend = function(origin, add) { + // Don't do anything if add isn't an object + if (!add || !isObject(add)) return origin; + + var keys = Object.keys(add); + var i = keys.length; + while (i--) { + origin[keys[i]] = add[keys[i]]; + } + return origin; + }; + + function hasOwnProperty(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); + } + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()), __webpack_require__(3))) + +/***/ }, +/* 3 */ +/***/ function(module, exports) { + + // shim for using process in browser + var process = module.exports = {}; + + // cached from whatever global is present so that test runners that stub it + // don't break things. But we need to wrap it in a try catch in case it is + // wrapped in strict mode code which doesn't define any globals. It's inside a + // function because try/catches deoptimize in certain engines. + + var cachedSetTimeout; + var cachedClearTimeout; + + function defaultSetTimout() { + throw new Error('setTimeout has not been defined'); + } + function defaultClearTimeout () { + throw new Error('clearTimeout has not been defined'); + } + (function () { + try { + if (typeof setTimeout === 'function') { + cachedSetTimeout = setTimeout; + } else { + cachedSetTimeout = defaultSetTimout; + } + } catch (e) { + cachedSetTimeout = defaultSetTimout; + } + try { + if (typeof clearTimeout === 'function') { + cachedClearTimeout = clearTimeout; + } else { + cachedClearTimeout = defaultClearTimeout; + } + } catch (e) { + cachedClearTimeout = defaultClearTimeout; + } + } ()) + function runTimeout(fun) { + if (cachedSetTimeout === setTimeout) { + //normal enviroments in sane situations + return setTimeout(fun, 0); + } + // if setTimeout wasn't available but was latter defined + if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { + cachedSetTimeout = setTimeout; + return setTimeout(fun, 0); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedSetTimeout(fun, 0); + } catch(e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedSetTimeout.call(null, fun, 0); + } catch(e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error + return cachedSetTimeout.call(this, fun, 0); + } + } + + + } + function runClearTimeout(marker) { + if (cachedClearTimeout === clearTimeout) { + //normal enviroments in sane situations + return clearTimeout(marker); + } + // if clearTimeout wasn't available but was latter defined + if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { + cachedClearTimeout = clearTimeout; + return clearTimeout(marker); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedClearTimeout(marker); + } catch (e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedClearTimeout.call(null, marker); + } catch (e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. + // Some versions of I.E. have different rules for clearTimeout vs setTimeout + return cachedClearTimeout.call(this, marker); + } + } + + + + } + var queue = []; + var draining = false; + var currentQueue; + var queueIndex = -1; + + function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } + } + + function drainQueue() { + if (draining) { + return; + } + var timeout = runTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + runClearTimeout(timeout); + } + + process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } + }; + + // v8 likes predictible objects + function Item(fun, array) { + this.fun = fun; + this.array = array; + } + Item.prototype.run = function () { + this.fun.apply(null, this.array); + }; + process.title = 'browser'; + process.browser = true; + process.env = {}; + process.argv = []; + process.version = ''; // empty string to avoid regexp issues + process.versions = {}; + + function noop() {} + + process.on = noop; + process.addListener = noop; + process.once = noop; + process.off = noop; + process.removeListener = noop; + process.removeAllListeners = noop; + process.emit = noop; + + process.binding = function (name) { + throw new Error('process.binding is not supported'); + }; + + process.cwd = function () { return '/' }; + process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); + }; + process.umask = function() { return 0; }; + + +/***/ }, +/* 4 */ +/***/ function(module, exports) { + + module.exports = function isBuffer(arg) { + return arg && typeof arg === 'object' + && typeof arg.copy === 'function' + && typeof arg.fill === 'function' + && typeof arg.readUInt8 === 'function'; + } + +/***/ }, +/* 5 */ +/***/ function(module, exports) { + + if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }; + } else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + var TempCtor = function () {} + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } + } + + +/***/ }, +/* 6 */ +/***/ function(module, exports, __webpack_require__) { + + var EventEmitter = __webpack_require__(1); + var Sequencer = __webpack_require__(7); + var Blocks = __webpack_require__(24); + var Thread = __webpack_require__(9); + var util = __webpack_require__(2); + + // Virtual I/O devices. + var Clock = __webpack_require__(79); + var Keyboard = __webpack_require__(80); + var Mouse = __webpack_require__(83); + + var defaultBlockPackages = { + scratch3_control: __webpack_require__(85), + scratch3_event: __webpack_require__(86), + scratch3_looks: __webpack_require__(87), + scratch3_motion: __webpack_require__(88), + scratch3_operators: __webpack_require__(89), + scratch3_pen: __webpack_require__(103), + scratch3_sound: __webpack_require__(90), + scratch3_sensing: __webpack_require__(91), + scratch3_data: __webpack_require__(92), + scratch3_procedures: __webpack_require__(93) + }; + + /** + * Manages targets, scripts, and the sequencer. + * @constructor + */ + var Runtime = function () { + // Bind event emitter + EventEmitter.call(this); + + /** + * Target management and storage. + * @type {Array.} + */ + this.targets = []; + + /** + * A list of threads that are currently running in the VM. + * Threads are added when execution starts and pruned when execution ends. + * @type {Array.} + */ + this.threads = []; + + /** @type {!Sequencer} */ + this.sequencer = new Sequencer(this); + + /** + * Storage container for flyout blocks. + * These will execute on `_editingTarget.` + * @type {!Blocks} + */ + this.flyoutBlocks = new Blocks(); + + /** + * Currently known editing target for the VM. + * @type {?Target} + */ + this._editingTarget = null; + + /** + * Map to look up a block primitive's implementation function by its opcode. + * This is a two-step lookup: package name first, then primitive name. + * @type {Object.} + */ + this._primitives = {}; + + /** + * Map to look up hat blocks' metadata. + * Keys are opcode for hat, values are metadata objects. + * @type {Object.} + */ + this._hats = {}; + + /** + * Currently known values for edge-activated hats. + * Keys are block ID for the hat; values are the currently known values. + * @type {Object.} + */ + this._edgeActivatedHatValues = {}; + + /** + * A list of script block IDs that were glowing during the previous frame. + * @type {!Array.} + */ + this._scriptGlowsPreviousFrame = []; + + /** + * Number of threads running during the previous frame + * @type {number} + */ + this._threadCount = 0; + + /** + * Currently known number of clones, used to enforce clone limit. + * @type {number} + */ + this._cloneCounter = 0; + + /** + * Whether the project is in "turbo mode." + * @type {Boolean} + */ + this.turboMode = false; + + /** + * Whether the project is in "compatibility mode" (30 TPS). + * @type {Boolean} + */ + this.compatibilityMode = false; + + /** + * A reference to the current runtime stepping interval, set + * by a `setInterval`. + * @type {!number} + */ + this._steppingInterval = null; + + /** + * Current length of a step. + * Changes as mode switches, and used by the sequencer to calculate + * WORK_TIME. + * @type {!number} + */ + this.currentStepTime = null; + + /** + * Whether any primitive has requested a redraw. + * Affects whether `Sequencer.stepThreads` will yield + * after stepping each thread. + * Reset on every frame. + * @type {boolean} + */ + this.redrawRequested = false; + + // Register all given block packages. + this._registerBlockPackages(); + + // Register and initialize "IO devices", containers for processing + // I/O related data. + /** @type {Object.} */ + this.ioDevices = { + clock: new Clock(), + keyboard: new Keyboard(this), + mouse: new Mouse(this) + }; + }; + + /** + * Inherit from EventEmitter + */ + util.inherits(Runtime, EventEmitter); + + /** + * Width of the stage, in pixels. + * @const {number} + */ + Runtime.STAGE_WIDTH = 480; + + /** + * Height of the stage, in pixels. + * @const {number} + */ + Runtime.STAGE_HEIGHT = 360; + + /** + * Event name for glowing a script. + * @const {string} + */ + Runtime.SCRIPT_GLOW_ON = 'SCRIPT_GLOW_ON'; + + /** + * Event name for unglowing a script. + * @const {string} + */ + Runtime.SCRIPT_GLOW_OFF = 'SCRIPT_GLOW_OFF'; + + /** + * Event name for glowing a block. + * @const {string} + */ + Runtime.BLOCK_GLOW_ON = 'BLOCK_GLOW_ON'; + + /** + * Event name for unglowing a block. + * @const {string} + */ + Runtime.BLOCK_GLOW_OFF = 'BLOCK_GLOW_OFF'; + + /** + * Event name for glowing the green flag + * @const {string} + */ + Runtime.PROJECT_RUN_START = 'PROJECT_RUN_START'; + + /** + * Event name for unglowing the green flag + * @const {string} + */ + Runtime.PROJECT_RUN_STOP = 'PROJECT_RUN_STOP'; + + /** + * Event name for visual value report. + * @const {string} + */ + Runtime.VISUAL_REPORT = 'VISUAL_REPORT'; + + /** + * Event name for sprite info report. + * @const {string} + */ + Runtime.SPRITE_INFO_REPORT = 'SPRITE_INFO_REPORT'; + + /** + * How rapidly we try to step threads by default, in ms. + */ + Runtime.THREAD_STEP_INTERVAL = 1000 / 60; + + /** + * In compatibility mode, how rapidly we try to step threads, in ms. + */ + Runtime.THREAD_STEP_INTERVAL_COMPATIBILITY = 1000 / 30; + + /** + * How many clones can be created at a time. + * @const {number} + */ + Runtime.MAX_CLONES = 300; + + // ----------------------------------------------------------------------------- + // ----------------------------------------------------------------------------- + + /** + * Register default block packages with this runtime. + * @todo Prefix opcodes with package name. + * @private + */ + Runtime.prototype._registerBlockPackages = function () { + for (var packageName in defaultBlockPackages) { + if (defaultBlockPackages.hasOwnProperty(packageName)) { + // @todo pass a different runtime depending on package privilege? + var packageObject = new (defaultBlockPackages[packageName])(this); + // Collect primitives from package. + if (packageObject.getPrimitives) { + var packagePrimitives = packageObject.getPrimitives(); + for (var op in packagePrimitives) { + if (packagePrimitives.hasOwnProperty(op)) { + this._primitives[op] = + packagePrimitives[op].bind(packageObject); + } + } + } + // Collect hat metadata from package. + if (packageObject.getHats) { + var packageHats = packageObject.getHats(); + for (var hatName in packageHats) { + if (packageHats.hasOwnProperty(hatName)) { + this._hats[hatName] = packageHats[hatName]; + } + } + } + } + } + }; + + /** + * Retrieve the function associated with the given opcode. + * @param {!string} opcode The opcode to look up. + * @return {Function} The function which implements the opcode. + */ + Runtime.prototype.getOpcodeFunction = function (opcode) { + return this._primitives[opcode]; + }; + + /** + * Return whether an opcode represents a hat block. + * @param {!string} opcode The opcode to look up. + * @return {Boolean} True if the op is known to be a hat. + */ + Runtime.prototype.getIsHat = function (opcode) { + return this._hats.hasOwnProperty(opcode); + }; + + /** + * Return whether an opcode represents an edge-activated hat block. + * @param {!string} opcode The opcode to look up. + * @return {Boolean} True if the op is known to be a edge-activated hat. + */ + Runtime.prototype.getIsEdgeActivatedHat = function (opcode) { + return this._hats.hasOwnProperty(opcode) && + this._hats[opcode].edgeActivated; + }; + + /** + * Update an edge-activated hat block value. + * @param {!string} blockId ID of hat to store value for. + * @param {*} newValue Value to store for edge-activated hat. + * @return {*} The old value for the edge-activated hat. + */ + Runtime.prototype.updateEdgeActivatedValue = function (blockId, newValue) { + var oldValue = this._edgeActivatedHatValues[blockId]; + this._edgeActivatedHatValues[blockId] = newValue; + return oldValue; + }; + + /** + * Clear all edge-activaed hat values. + */ + Runtime.prototype.clearEdgeActivatedValues = function () { + this._edgeActivatedHatValues = {}; + }; + + /** + * Attach the renderer + * @param {!RenderWebGL} renderer The renderer to attach + */ + Runtime.prototype.attachRenderer = function (renderer) { + this.renderer = renderer; + }; + + /** + * Attach the audio engine + * @param {!AudioEngine} audioEngine The audio engine to attach + */ + Runtime.prototype.attachAudioEngine = function (audioEngine) { + this.audioEngine = audioEngine; + }; + + // ----------------------------------------------------------------------------- + // ----------------------------------------------------------------------------- + + /** + * Create a thread and push it to the list of threads. + * @param {!string} id ID of block that starts the stack. + * @param {!Target} target Target to run thread on. + * @return {!Thread} The newly created thread. + */ + Runtime.prototype._pushThread = function (id, target) { + var thread = new Thread(id); + thread.target = target; + thread.pushStack(id); + this.threads.push(thread); + return thread; + }; + + /** + * Remove a thread from the list of threads. + * @param {?Thread} thread Thread object to remove from actives + */ + Runtime.prototype._removeThread = function (thread) { + // Inform sequencer to stop executing that thread. + this.sequencer.retireThread(thread); + // Remove from the list. + var i = this.threads.indexOf(thread); + if (i > -1) { + this.threads.splice(i, 1); + } + }; + + /** + * Restart a thread in place, maintaining its position in the list of threads. + * This is used by `startHats` to and is necessary to ensure 2.0-like execution order. + * Test project: https://scratch.mit.edu/projects/130183108/ + * @param {!Thread} thread Thread object to restart. + */ + Runtime.prototype._restartThread = function (thread) { + var newThread = new Thread(thread.topBlock); + newThread.target = thread.target; + newThread.pushStack(thread.topBlock); + var i = this.threads.indexOf(thread); + if (i > -1) { + this.threads[i] = newThread; + } else { + this.threads.push(thread); + } + }; + + /** + * Return whether a thread is currently active/running. + * @param {?Thread} thread Thread object to check. + * @return {Boolean} True if the thread is active/running. + */ + Runtime.prototype.isActiveThread = function (thread) { + return this.threads.indexOf(thread) > -1; + }; + + /** + * Toggle a script. + * @param {!string} topBlockId ID of block that starts the script. + */ + Runtime.prototype.toggleScript = function (topBlockId) { + // Remove any existing thread. + for (var i = 0; i < this.threads.length; i++) { + if (this.threads[i].topBlock === topBlockId) { + this._removeThread(this.threads[i]); + return; + } + } + // Otherwise add it. + this._pushThread(topBlockId, this._editingTarget); + }; + + /** + * Run a function `f` for all scripts in a workspace. + * `f` will be called with two parameters: + * - the top block ID of the script. + * - the target that owns the script. + * @param {!Function} f Function to call for each script. + * @param {Target=} optTarget Optionally, a target to restrict to. + */ + Runtime.prototype.allScriptsDo = function (f, optTarget) { + var targets = this.targets; + if (optTarget) { + targets = [optTarget]; + } + for (var t = 0; t < targets.length; t++) { + var target = targets[t]; + var scripts = target.blocks.getScripts(); + for (var j = 0; j < scripts.length; j++) { + var topBlockId = scripts[j]; + f(topBlockId, target); + } + } + }; + + /** + * Start all relevant hats. + * @param {!string} requestedHatOpcode Opcode of hats to start. + * @param {Object=} optMatchFields Optionally, fields to match on the hat. + * @param {Target=} optTarget Optionally, a target to restrict to. + * @return {Array.} List of threads started by this function. + */ + Runtime.prototype.startHats = function (requestedHatOpcode, + optMatchFields, optTarget) { + if (!this._hats.hasOwnProperty(requestedHatOpcode)) { + // No known hat with this opcode. + return; + } + var instance = this; + var newThreads = []; + // Consider all scripts, looking for hats with opcode `requestedHatOpcode`. + this.allScriptsDo(function (topBlockId, target) { + var potentialHatOpcode = target.blocks.getBlock(topBlockId).opcode; + if (potentialHatOpcode !== requestedHatOpcode) { + // Not the right hat. + return; + } + + // Match any requested fields. + // For example: ensures that broadcasts match. + // This needs to happen before the block is evaluated + // (i.e., before the predicate can be run) because "broadcast and wait" + // needs to have a precise collection of started threads. + var hatFields = target.blocks.getFields(topBlockId); + + // If no fields are present, check inputs (horizontal blocks) + if (Object.keys(hatFields).length === 0) { + var hatInputs = target.blocks.getInputs(topBlockId); + for (var input in hatInputs) { + var id = hatInputs[input].block; + var fields = target.blocks.getFields(id); + hatFields = Object.assign(fields, hatFields); + } + } + + if (optMatchFields) { + for (var matchField in optMatchFields) { + if (hatFields[matchField].value !== + optMatchFields[matchField]) { + // Field mismatch. + return; + } + } + } + + // Look up metadata for the relevant hat. + var hatMeta = instance._hats[requestedHatOpcode]; + if (hatMeta.restartExistingThreads) { + // If `restartExistingThreads` is true, we should stop + // any existing threads starting with the top block. + for (var i = 0; i < instance.threads.length; i++) { + if (instance.threads[i].topBlock === topBlockId && + instance.threads[i].target === target) { + instance._restartThread(instance.threads[i]); + return; + } + } + } else { + // If `restartExistingThreads` is false, we should + // give up if any threads with the top block are running. + for (var j = 0; j < instance.threads.length; j++) { + if (instance.threads[j].topBlock === topBlockId && + instance.threads[j].target === target) { + // Some thread is already running. + return; + } + } + } + // Start the thread with this top block. + newThreads.push(instance._pushThread(topBlockId, target)); + }, optTarget); + return newThreads; + }; + + /** + * Dispose all targets. Return to clean state. + */ + Runtime.prototype.dispose = function () { + this.stopAll(); + this.targets.map(this.disposeTarget, this); + }; + + /** + * Dispose of a target. + * @param {!Target} disposingTarget Target to dispose of. + */ + Runtime.prototype.disposeTarget = function (disposingTarget) { + this.targets = this.targets.filter(function (target) { + if (disposingTarget !== target) return true; + // Allow target to do dispose actions. + target.dispose(); + // Remove from list of targets. + return false; + }); + }; + + /** + * Stop any threads acting on the target. + * @param {!Target} target Target to stop threads for. + * @param {Thread=} optThreadException Optional thread to skip. + */ + Runtime.prototype.stopForTarget = function (target, optThreadException) { + // Stop any threads on the target. + for (var i = 0; i < this.threads.length; i++) { + if (this.threads[i] === optThreadException) { + continue; + } + if (this.threads[i].target === target) { + this._removeThread(this.threads[i]); + } + } + }; + + /** + * Start all threads that start with the green flag. + */ + Runtime.prototype.greenFlag = function () { + this.stopAll(); + this.ioDevices.clock.resetProjectTimer(); + this.clearEdgeActivatedValues(); + // Inform all targets of the green flag. + for (var i = 0; i < this.targets.length; i++) { + this.targets[i].onGreenFlag(); + } + this.startHats('event_whenflagclicked'); + }; + + /** + * Stop "everything." + */ + Runtime.prototype.stopAll = function () { + // Dispose all clones. + var newTargets = []; + for (var i = 0; i < this.targets.length; i++) { + this.targets[i].onStopAll(); + if (this.targets[i].hasOwnProperty('isOriginal') && + !this.targets[i].isOriginal) { + this.targets[i].dispose(); + } else { + newTargets.push(this.targets[i]); + } + } + this.targets = newTargets; + // Dispose all threads. + var threadsCopy = this.threads.slice(); + while (threadsCopy.length > 0) { + var poppedThread = threadsCopy.pop(); + this._removeThread(poppedThread); + } + }; + + /** + * Repeatedly run `sequencer.stepThreads` and filter out + * inactive threads after each iteration. + */ + Runtime.prototype._step = function () { + // Find all edge-activated hats, and add them to threads to be evaluated. + for (var hatType in this._hats) { + var hat = this._hats[hatType]; + if (hat.edgeActivated) { + this.startHats(hatType); + } + } + this.redrawRequested = false; + var doneThreads = this.sequencer.stepThreads(); + this._updateGlows(doneThreads); + this._setThreadCount(this.threads.length + doneThreads.length); + if (this.renderer) { + // @todo: Only render when this.redrawRequested or clones rendered. + this.renderer.draw(); + } + }; + + /** + * Set the current editing target known by the runtime. + * @param {!Target} editingTarget New editing target. + */ + Runtime.prototype.setEditingTarget = function (editingTarget) { + this._editingTarget = editingTarget; + // Script glows must be cleared. + this._scriptGlowsPreviousFrame = []; + this._updateGlows(); + this.spriteInfoReport(editingTarget); + }; + + /** + * Set whether we are in 30 TPS compatibility mode. + * @param {boolean} compatibilityModeOn True iff in compatibility mode. + */ + Runtime.prototype.setCompatibilityMode = function (compatibilityModeOn) { + this.compatibilityMode = compatibilityModeOn; + if (this._steppingInterval) { + clearInterval(this._steppingInterval); + this.start(); + } + }; + + /** + * Emit glows/glow clears for scripts after a single tick. + * Looks at `this.threads` and notices which have turned on/off new glows. + * @param {Array.=} optExtraThreads Optional list of inactive threads. + */ + Runtime.prototype._updateGlows = function (optExtraThreads) { + var searchThreads = []; + searchThreads.push.apply(searchThreads, this.threads); + if (optExtraThreads) { + searchThreads.push.apply(searchThreads, optExtraThreads); + } + // Set of scripts that request a glow this frame. + var requestedGlowsThisFrame = []; + // Final set of scripts glowing during this frame. + var finalScriptGlows = []; + // Find all scripts that should be glowing. + for (var i = 0; i < searchThreads.length; i++) { + var thread = searchThreads[i]; + var target = thread.target; + if (target === this._editingTarget) { + var blockForThread = thread.blockGlowInFrame; + if (thread.requestScriptGlowInFrame) { + var script = target.blocks.getTopLevelScript(blockForThread); + if (!script) { + // Attempt to find in flyout blocks. + script = this.flyoutBlocks.getTopLevelScript( + blockForThread + ); + } + if (script) { + requestedGlowsThisFrame.push(script); + } + } + } + } + // Compare to previous frame. + for (var j = 0; j < this._scriptGlowsPreviousFrame.length; j++) { + var previousFrameGlow = this._scriptGlowsPreviousFrame[j]; + if (requestedGlowsThisFrame.indexOf(previousFrameGlow) < 0) { + // Glow turned off. + this.glowScript(previousFrameGlow, false); + } else { + // Still glowing. + finalScriptGlows.push(previousFrameGlow); + } + } + for (var k = 0; k < requestedGlowsThisFrame.length; k++) { + var currentFrameGlow = requestedGlowsThisFrame[k]; + if (this._scriptGlowsPreviousFrame.indexOf(currentFrameGlow) < 0) { + // Glow turned on. + this.glowScript(currentFrameGlow, true); + finalScriptGlows.push(currentFrameGlow); + } + } + this._scriptGlowsPreviousFrame = finalScriptGlows; + }; + + /** + * Emit run start/stop after each tick. Emits when `this.threads.length` goes + * between non-zero and zero + * + * @param {number} threadCount The new threadCount + */ + Runtime.prototype._setThreadCount = function (threadCount) { + if (this._threadCount === 0 && threadCount > 0) { + this.emit(Runtime.PROJECT_RUN_START); + } + if (this._threadCount > 0 && threadCount === 0) { + this.emit(Runtime.PROJECT_RUN_STOP); + } + this._threadCount = threadCount; + }; + + /** + * "Quiet" a script's glow: stop the VM from generating glow/unglow events + * about that script. Use when a script has just been deleted, but we may + * still be tracking glow data about it. + * @param {!string} scriptBlockId Id of top-level block in script to quiet. + */ + Runtime.prototype.quietGlow = function (scriptBlockId) { + var index = this._scriptGlowsPreviousFrame.indexOf(scriptBlockId); + if (index > -1) { + this._scriptGlowsPreviousFrame.splice(index, 1); + } + }; + + /** + * Emit feedback for block glowing (used in the sequencer). + * @param {?string} blockId ID for the block to update glow + * @param {boolean} isGlowing True to turn on glow; false to turn off. + */ + Runtime.prototype.glowBlock = function (blockId, isGlowing) { + if (isGlowing) { + this.emit(Runtime.BLOCK_GLOW_ON, {id: blockId}); + } else { + this.emit(Runtime.BLOCK_GLOW_OFF, {id: blockId}); + } + }; + + /** + * Emit feedback for script glowing. + * @param {?string} topBlockId ID for the top block to update glow + * @param {boolean} isGlowing True to turn on glow; false to turn off. + */ + Runtime.prototype.glowScript = function (topBlockId, isGlowing) { + if (isGlowing) { + this.emit(Runtime.SCRIPT_GLOW_ON, {id: topBlockId}); + } else { + this.emit(Runtime.SCRIPT_GLOW_OFF, {id: topBlockId}); + } + }; + + /** + * Emit value for reporter to show in the blocks. + * @param {string} blockId ID for the block. + * @param {string} value Value to show associated with the block. + */ + Runtime.prototype.visualReport = function (blockId, value) { + this.emit(Runtime.VISUAL_REPORT, {id: blockId, value: String(value)}); + }; + + /** + * Emit a sprite info report if the provided target is the original sprite + * @param {!Target} target Target to report sprite info for. + */ + Runtime.prototype.spriteInfoReport = function (target) { + if (!target.isOriginal) return; + + this.emit(Runtime.SPRITE_INFO_REPORT, target.toJSON()); + }; + + /** + * Get a target by its id. + * @param {string} targetId Id of target to find. + * @return {?Target} The target, if found. + */ + Runtime.prototype.getTargetById = function (targetId) { + for (var i = 0; i < this.targets.length; i++) { + var target = this.targets[i]; + if (target.id === targetId) { + return target; + } + } + }; + + /** + * Get the first original (non-clone-block-created) sprite given a name. + * @param {string} spriteName Name of sprite to look for. + * @return {?Target} Target representing a sprite of the given name. + */ + Runtime.prototype.getSpriteTargetByName = function (spriteName) { + for (var i = 0; i < this.targets.length; i++) { + var target = this.targets[i]; + if (target.sprite && target.sprite.name === spriteName) { + return target; + } + } + }; + + /** + * Update the clone counter to track how many clones are created. + * @param {number} changeAmount How many clones have been created/destroyed. + */ + Runtime.prototype.changeCloneCounter = function (changeAmount) { + this._cloneCounter += changeAmount; + }; + + /** + * Return whether there are clones available. + * @return {boolean} True until the number of clones hits Runtime.MAX_CLONES. + */ + Runtime.prototype.clonesAvailable = function () { + return this._cloneCounter < Runtime.MAX_CLONES; + }; + + /** + * Get a target representing the Scratch stage, if one exists. + * @return {?Target} The target, if found. + */ + Runtime.prototype.getTargetForStage = function () { + for (var i = 0; i < this.targets.length; i++) { + var target = this.targets[i]; + if (target.isStage) { + return target; + } + } + }; + + /** + * Tell the runtime to request a redraw. + * Use after a clone/sprite has completed some visible operation on the stage. + */ + Runtime.prototype.requestRedraw = function () { + this.redrawRequested = true; + }; + + /** + * Set up timers to repeatedly step in a browser. + */ + Runtime.prototype.start = function () { + var interval = Runtime.THREAD_STEP_INTERVAL; + if (this.compatibilityMode) { + interval = Runtime.THREAD_STEP_INTERVAL_COMPATIBILITY; + } + this.currentStepTime = interval; + this._steppingInterval = setInterval(function () { + this._step(); + }.bind(this), interval); + }; + + module.exports = Runtime; + + +/***/ }, +/* 7 */ +/***/ function(module, exports, __webpack_require__) { + + var Timer = __webpack_require__(8); + var Thread = __webpack_require__(9); + var execute = __webpack_require__(10); + + var Sequencer = function (runtime) { + /** + * A utility timer for timing thread sequencing. + * @type {!Timer} + */ + this.timer = new Timer(); + + /** + * Reference to the runtime owning this sequencer. + * @type {!Runtime} + */ + this.runtime = runtime; + }; + + /** + * Time to run a warp-mode thread, in ms. + * @type {number} + */ + Sequencer.WARP_TIME = 500; + + /** + * Step through all threads in `this.runtime.threads`, running them in order. + * @return {Array.} List of inactive threads after stepping. + */ + Sequencer.prototype.stepThreads = function () { + // Work time is 75% of the thread stepping interval. + var WORK_TIME = 0.75 * this.runtime.currentStepTime; + // Start counting toward WORK_TIME. + this.timer.start(); + // Count of active threads. + var numActiveThreads = Infinity; + // Whether `stepThreads` has run through a full single tick. + var ranFirstTick = false; + var doneThreads = []; + // Conditions for continuing to stepping threads: + // 1. We must have threads in the list, and some must be active. + // 2. Time elapsed must be less than WORK_TIME. + // 3. Either turbo mode, or no redraw has been requested by a primitive. + while (this.runtime.threads.length > 0 && + numActiveThreads > 0 && + this.timer.timeElapsed() < WORK_TIME && + (this.runtime.turboMode || !this.runtime.redrawRequested)) { + numActiveThreads = 0; + // Attempt to run each thread one time. + for (var i = 0; i < this.runtime.threads.length; i++) { + var activeThread = this.runtime.threads[i]; + if (activeThread.stack.length === 0 || + activeThread.status === Thread.STATUS_DONE) { + // Finished with this thread. + if (doneThreads.indexOf(activeThread) < 0) { + doneThreads.push(activeThread); + } + continue; + } + if (activeThread.status === Thread.STATUS_YIELD_TICK && + !ranFirstTick) { + // Clear single-tick yield from the last call of `stepThreads`. + activeThread.status = Thread.STATUS_RUNNING; + } + if (activeThread.status === Thread.STATUS_RUNNING || + activeThread.status === Thread.STATUS_YIELD) { + // Normal-mode thread: step. + this.stepThread(activeThread); + activeThread.warpTimer = null; + } + if (activeThread.status === Thread.STATUS_RUNNING) { + numActiveThreads++; + } + } + // We successfully ticked once. Prevents running STATUS_YIELD_TICK + // threads on the next tick. + ranFirstTick = true; + } + // Filter inactive threads from `this.runtime.threads`. + this.runtime.threads = this.runtime.threads.filter(function (thread) { + if (doneThreads.indexOf(thread) > -1) { + return false; + } + return true; + }); + return doneThreads; + }; + + /** + * Step the requested thread for as long as necessary. + * @param {!Thread} thread Thread object to step. + */ + Sequencer.prototype.stepThread = function (thread) { + var currentBlockId = thread.peekStack(); + if (!currentBlockId) { + // A "null block" - empty branch. + thread.popStack(); + } + while (thread.peekStack()) { + var isWarpMode = thread.peekStackFrame().warpMode; + if (isWarpMode && !thread.warpTimer) { + // Initialize warp-mode timer if it hasn't been already. + // This will start counting the thread toward `Sequencer.WARP_TIME`. + thread.warpTimer = new Timer(); + thread.warpTimer.start(); + } + // Execute the current block. + // Save the current block ID to notice if we did control flow. + currentBlockId = thread.peekStack(); + execute(this, thread); + thread.blockGlowInFrame = currentBlockId; + // If the thread has yielded or is waiting, yield to other threads. + if (thread.status === Thread.STATUS_YIELD) { + // Mark as running for next iteration. + thread.status = Thread.STATUS_RUNNING; + // In warp mode, yielded blocks are re-executed immediately. + if (isWarpMode && + thread.warpTimer.timeElapsed() <= Sequencer.WARP_TIME) { + continue; + } + return; + } else if (thread.status === Thread.STATUS_PROMISE_WAIT) { + // A promise was returned by the primitive. Yield the thread + // until the promise resolves. Promise resolution should reset + // thread.status to Thread.STATUS_RUNNING. + return; + } + // If no control flow has happened, switch to next block. + if (thread.peekStack() === currentBlockId) { + thread.goToNextBlock(); + } + // If no next block has been found at this point, look on the stack. + while (!thread.peekStack()) { + thread.popStack(); + + if (thread.stack.length === 0) { + // No more stack to run! + thread.status = Thread.STATUS_DONE; + return; + } + + var stackFrame = thread.peekStackFrame(); + isWarpMode = stackFrame.warpMode; + + if (stackFrame.isLoop) { + // The current level of the stack is marked as a loop. + // Return to yield for the frame/tick in general. + // Unless we're in warp mode - then only return if the + // warp timer is up. + if (!isWarpMode || + thread.warpTimer.timeElapsed() > Sequencer.WARP_TIME) { + // Don't do anything to the stack, since loops need + // to be re-executed. + return; + } else { + // Don't go to the next block for this level of the stack, + // since loops need to be re-executed. + continue; + } + } else if (stackFrame.waitingReporter) { + // This level of the stack was waiting for a value. + // This means a reporter has just returned - so don't go + // to the next block for this level of the stack. + return; + } + // Get next block of existing block on the stack. + thread.goToNextBlock(); + } + } + }; + + /** + * Step a thread into a block's branch. + * @param {!Thread} thread Thread object to step to branch. + * @param {Number} branchNum Which branch to step to (i.e., 1, 2). + * @param {Boolean} isLoop Whether this block is a loop. + */ + Sequencer.prototype.stepToBranch = function (thread, branchNum, isLoop) { + if (!branchNum) { + branchNum = 1; + } + var currentBlockId = thread.peekStack(); + var branchId = thread.target.blocks.getBranch( + currentBlockId, + branchNum + ); + thread.peekStackFrame().isLoop = isLoop; + if (branchId) { + // Push branch ID to the thread's stack. + thread.pushStack(branchId); + } else { + thread.pushStack(null); + } + }; + + /** + * Step a procedure. + * @param {!Thread} thread Thread object to step to procedure. + * @param {!string} procedureCode Procedure code of procedure to step to. + */ + Sequencer.prototype.stepToProcedure = function (thread, procedureCode) { + var definition = thread.target.blocks.getProcedureDefinition(procedureCode); + if (!definition) { + return; + } + // Check if the call is recursive. + // If so, set the thread to yield after pushing. + var isRecursive = thread.isRecursiveCall(procedureCode); + // To step to a procedure, we put its definition on the stack. + // Execution for the thread will proceed through the definition hat + // and on to the main definition of the procedure. + // When that set of blocks finishes executing, it will be popped + // from the stack by the sequencer, returning control to the caller. + thread.pushStack(definition); + // In known warp-mode threads, only yield when time is up. + if (thread.peekStackFrame().warpMode && + thread.warpTimer.timeElapsed() > Sequencer.WARP_TIME) { + thread.status = Thread.STATUS_YIELD; + } else { + // Look for warp-mode flag on definition, and set the thread + // to warp-mode if needed. + var definitionBlock = thread.target.blocks.getBlock(definition); + var doWarp = definitionBlock.mutation.warp; + if (doWarp) { + thread.peekStackFrame().warpMode = true; + } else { + // In normal-mode threads, yield any time we have a recursive call. + if (isRecursive) { + thread.status = Thread.STATUS_YIELD; + } + } + } + }; + + /** + * Retire a thread in the middle, without considering further blocks. + * @param {!Thread} thread Thread object to retire. + */ + Sequencer.prototype.retireThread = function (thread) { + thread.stack = []; + thread.stackFrame = []; + thread.requestScriptGlowInFrame = false; + thread.status = Thread.STATUS_DONE; + }; + + module.exports = Sequencer; + + +/***/ }, +/* 8 */ +/***/ function(module, exports) { + + /** + * @fileoverview + * A utility for accurately measuring time. + * To use: + * --- + * var timer = new Timer(); + * timer.start(); + * ... pass some time ... + * var timeDifference = timer.timeElapsed(); + * --- + * Or, you can use the `time` and `relativeTime` + * to do some measurement yourself. + */ + + /** + * @constructor + */ + var Timer = function () {}; + + /** + * Used to store the start time of a timer action. + * Updated when calling `timer.start`. + */ + Timer.prototype.startTime = 0; + + /** + * Return the currently known absolute time, in ms precision. + * @returns {number} ms elapsed since 1 January 1970 00:00:00 UTC. + */ + Timer.prototype.time = function () { + if (Date.now) { + return Date.now(); + } else { + return new Date().getTime(); + } + }; + + /** + * Returns a time accurate relative to other times produced by this function. + * If possible, will use sub-millisecond precision. + * If not, will use millisecond precision. + * Not guaranteed to produce the same absolute values per-system. + * @returns {number} ms-scale accurate time relative to other relative times. + */ + Timer.prototype.relativeTime = function () { + if (typeof self !== 'undefined' && + self.performance && 'now' in self.performance) { + return self.performance.now(); + } else { + return this.time(); + } + }; + + /** + * Start a timer for measuring elapsed time, + * at the most accurate precision possible. + */ + Timer.prototype.start = function () { + this.startTime = this.relativeTime(); + }; + + /** + * Check time elapsed since `timer.start` was called. + * @returns {number} Time elapsed, in ms (possibly sub-ms precision). + */ + Timer.prototype.timeElapsed = function () { + return this.relativeTime() - this.startTime; + }; + + module.exports = Timer; + + +/***/ }, +/* 9 */ +/***/ function(module, exports) { + + /** + * A thread is a running stack context and all the metadata needed. + * @param {?string} firstBlock First block to execute in the thread. + * @constructor + */ + var Thread = function (firstBlock) { + /** + * ID of top block of the thread + * @type {!string} + */ + this.topBlock = firstBlock; + + /** + * Stack for the thread. When the sequencer enters a control structure, + * the block is pushed onto the stack so we know where to exit. + * @type {Array.} + */ + this.stack = []; + + /** + * Stack frames for the thread. Store metadata for the executing blocks. + * @type {Array.} + */ + this.stackFrames = []; + + /** + * Status of the thread, one of three states (below) + * @type {number} + */ + this.status = 0; /* Thread.STATUS_RUNNING */ + + /** + * Target of this thread. + * @type {?Target} + */ + this.target = null; + + /** + * Whether the thread requests its script to glow during this frame. + * @type {boolean} + */ + this.requestScriptGlowInFrame = false; + + /** + * Which block ID should glow during this frame, if any. + * @type {?string} + */ + this.blockGlowInFrame = null; + + /** + * A timer for when the thread enters warp mode. + * Substitutes the sequencer's count toward WORK_TIME on a per-thread basis. + * @type {?Timer} + */ + this.warpTimer = null; + }; + + /** + * Thread status for initialized or running thread. + * This is the default state for a thread - execution should run normally, + * stepping from block to block. + * @const + */ + Thread.STATUS_RUNNING = 0; + + /** + * Threads are in this state when a primitive is waiting on a promise; + * execution is paused until the promise changes thread status. + * @const + */ + Thread.STATUS_PROMISE_WAIT = 1; + + /** + * Thread status for yield. + * @const + */ + Thread.STATUS_YIELD = 2; + + /** + * Thread status for a single-tick yield. This will be cleared when the + * thread is resumed. + * @const + */ + Thread.STATUS_YIELD_TICK = 3; + + /** + * Thread status for a finished/done thread. + * Thread is in this state when there are no more blocks to execute. + * @const + */ + Thread.STATUS_DONE = 4; + + /** + * Push stack and update stack frames appropriately. + * @param {string} blockId Block ID to push to stack. + */ + Thread.prototype.pushStack = function (blockId) { + this.stack.push(blockId); + // Push an empty stack frame, if we need one. + // Might not, if we just popped the stack. + if (this.stack.length > this.stackFrames.length) { + // Copy warp mode from any higher level. + var warpMode = false; + if (this.stackFrames[this.stackFrames.length - 1]) { + warpMode = this.stackFrames[this.stackFrames.length - 1].warpMode; + } + this.stackFrames.push({ + isLoop: false, // Whether this level of the stack is a loop. + warpMode: warpMode, // Whether this level is in warp mode. + reported: {}, // Collects reported input values. + waitingReporter: null, // Name of waiting reporter. + params: {}, // Procedure parameters. + executionContext: {} // A context passed to block implementations. + }); + } + }; + + /** + * Pop last block on the stack and its stack frame. + * @return {string} Block ID popped from the stack. + */ + Thread.prototype.popStack = function () { + this.stackFrames.pop(); + return this.stack.pop(); + }; + + /** + * Get top stack item. + * @return {?string} Block ID on top of stack. + */ + Thread.prototype.peekStack = function () { + return this.stack[this.stack.length - 1]; + }; + + + /** + * Get top stack frame. + * @return {?Object} Last stack frame stored on this thread. + */ + Thread.prototype.peekStackFrame = function () { + return this.stackFrames[this.stackFrames.length - 1]; + }; + + /** + * Get stack frame above the current top. + * @return {?Object} Second to last stack frame stored on this thread. + */ + Thread.prototype.peekParentStackFrame = function () { + return this.stackFrames[this.stackFrames.length - 2]; + }; + + /** + * Push a reported value to the parent of the current stack frame. + * @param {*} value Reported value to push. + */ + Thread.prototype.pushReportedValue = function (value) { + var parentStackFrame = this.peekParentStackFrame(); + if (parentStackFrame) { + var waitingReporter = parentStackFrame.waitingReporter; + parentStackFrame.reported[waitingReporter] = value; + } + }; + + /** + * Add a parameter to the stack frame. + * Use when calling a procedure with parameter values. + * @param {!string} paramName Name of parameter. + * @param {*} value Value to set for parameter. + */ + Thread.prototype.pushParam = function (paramName, value) { + var stackFrame = this.peekStackFrame(); + stackFrame.params[paramName] = value; + }; + + /** + * Get a parameter at the lowest possible level of the stack. + * @param {!string} paramName Name of parameter. + * @return {*} value Value for parameter. + */ + Thread.prototype.getParam = function (paramName) { + for (var i = this.stackFrames.length - 1; i >= 0; i--) { + var frame = this.stackFrames[i]; + if (frame.params.hasOwnProperty(paramName)) { + return frame.params[paramName]; + } + } + return null; + }; + + /** + * Whether the current execution of a thread is at the top of the stack. + * @return {Boolean} True if execution is at top of the stack. + */ + Thread.prototype.atStackTop = function () { + return this.peekStack() === this.topBlock; + }; + + + /** + * Switch the thread to the next block at the current level of the stack. + * For example, this is used in a standard sequence of blocks, + * where execution proceeds from one block to the next. + */ + Thread.prototype.goToNextBlock = function () { + var nextBlockId = this.target.blocks.getNextBlock(this.peekStack()); + // Copy warp mode to next block. + var warpMode = this.peekStackFrame().warpMode; + // The current block is on the stack - pop it and push the next. + // Note that this could push `null` - that is handled by the sequencer. + this.popStack(); + this.pushStack(nextBlockId); + if (this.peekStackFrame()) { + this.peekStackFrame().warpMode = warpMode; + } + }; + + /** + * Attempt to determine whether a procedure call is recursive, + * by examining the stack. + * @param {!string} procedureCode Procedure code of procedure being called. + * @return {boolean} True if the call appears recursive. + */ + Thread.prototype.isRecursiveCall = function (procedureCode) { + var callCount = 5; // Max number of enclosing procedure calls to examine. + var sp = this.stack.length - 1; + for (var i = sp - 1; i >= 0; i--) { + var block = this.target.blocks.getBlock(this.stack[i]); + if (block.opcode === 'procedures_callnoreturn' && + block.mutation.proccode === procedureCode) { + return true; + } + if (--callCount < 0) return false; + } + return false; + }; + + module.exports = Thread; + + +/***/ }, +/* 10 */ +/***/ function(module, exports, __webpack_require__) { + + var log = __webpack_require__(11); + var Thread = __webpack_require__(9); + + /** + * Utility function to determine if a value is a Promise. + * @param {*} value Value to check for a Promise. + * @return {Boolean} True if the value appears to be a Promise. + */ + var isPromise = function (value) { + return value && value.then && typeof value.then === 'function'; + }; + + /** + * Execute a block. + * @param {!Sequencer} sequencer Which sequencer is executing. + * @param {!Thread} thread Thread which to read and execute. + */ + var execute = function (sequencer, thread) { + var runtime = sequencer.runtime; + var target = thread.target; + + // Current block to execute is the one on the top of the stack. + var currentBlockId = thread.peekStack(); + var currentStackFrame = thread.peekStackFrame(); + + // Check where the block lives: target blocks or flyout blocks. + var targetHasBlock = ( + typeof target.blocks.getBlock(currentBlockId) !== 'undefined' + ); + var flyoutHasBlock = ( + typeof runtime.flyoutBlocks.getBlock(currentBlockId) !== 'undefined' + ); + + // Stop if block or target no longer exists. + if (!target || (!targetHasBlock && !flyoutHasBlock)) { + // No block found: stop the thread; script no longer exists. + sequencer.retireThread(thread); + return; + } + + // Query info about the block. + var blockContainer = null; + if (targetHasBlock) { + blockContainer = target.blocks; + } else { + blockContainer = runtime.flyoutBlocks; + } + var opcode = blockContainer.getOpcode(currentBlockId); + var fields = blockContainer.getFields(currentBlockId); + var inputs = blockContainer.getInputs(currentBlockId); + var blockFunction = runtime.getOpcodeFunction(opcode); + var isHat = runtime.getIsHat(opcode); + + + if (!opcode) { + log.warn('Could not get opcode for block: ' + currentBlockId); + return; + } + + /** + * Handle any reported value from the primitive, either directly returned + * or after a promise resolves. + * @param {*} resolvedValue Value eventually returned from the primitive. + */ + var handleReport = function (resolvedValue) { + thread.pushReportedValue(resolvedValue); + if (isHat) { + // Hat predicate was evaluated. + if (runtime.getIsEdgeActivatedHat(opcode)) { + // If this is an edge-activated hat, only proceed if + // the value is true and used to be false. + var oldEdgeValue = runtime.updateEdgeActivatedValue( + currentBlockId, + resolvedValue + ); + var edgeWasActivated = !oldEdgeValue && resolvedValue; + if (!edgeWasActivated) { + sequencer.retireThread(thread); + } + } else { + // Not an edge-activated hat: retire the thread + // if predicate was false. + if (!resolvedValue) { + sequencer.retireThread(thread); + } + } + } else { + // In a non-hat, report the value visually if necessary if + // at the top of the thread stack. + if (typeof resolvedValue !== 'undefined' && thread.atStackTop()) { + runtime.visualReport(currentBlockId, resolvedValue); + } + // Finished any yields. + thread.status = Thread.STATUS_RUNNING; + } + }; + + // Hats and single-field shadows are implemented slightly differently + // from regular blocks. + // For hats: if they have an associated block function, + // it's treated as a predicate; if not, execution will proceed as a no-op. + // For single-field shadows: If the block has a single field, and no inputs, + // immediately return the value of the field. + if (!blockFunction) { + if (isHat) { + // Skip through the block (hat with no predicate). + return; + } else { + if (Object.keys(fields).length === 1 && + Object.keys(inputs).length === 0) { + // One field and no inputs - treat as arg. + for (var fieldKey in fields) { // One iteration. + handleReport(fields[fieldKey].value); + } + } else { + log.warn('Could not get implementation for opcode: ' + + opcode); + } + thread.requestScriptGlowInFrame = true; + return; + } + } + + // Generate values for arguments (inputs). + var argValues = {}; + + // Add all fields on this block to the argValues. + for (var fieldName in fields) { + argValues[fieldName] = fields[fieldName].value; + } + + // Recursively evaluate input blocks. + for (var inputName in inputs) { + var input = inputs[inputName]; + var inputBlockId = input.block; + // Is there no value for this input waiting in the stack frame? + if (typeof currentStackFrame.reported[inputName] === 'undefined' && + inputBlockId) { + // If there's not, we need to evaluate the block. + // Push to the stack to evaluate the reporter block. + thread.pushStack(inputBlockId); + // Save name of input for `Thread.pushReportedValue`. + currentStackFrame.waitingReporter = inputName; + // Actually execute the block. + execute(sequencer, thread); + if (thread.status === Thread.STATUS_PROMISE_WAIT) { + return; + } else { + // Execution returned immediately, + // and presumably a value was reported, so pop the stack. + currentStackFrame.waitingReporter = null; + thread.popStack(); + } + } + argValues[inputName] = currentStackFrame.reported[inputName]; + } + + // Add any mutation to args (e.g., for procedures). + var mutation = blockContainer.getMutation(currentBlockId); + if (mutation) { + argValues.mutation = mutation; + } + + // If we've gotten this far, all of the input blocks are evaluated, + // and `argValues` is fully populated. So, execute the block primitive. + // First, clear `currentStackFrame.reported`, so any subsequent execution + // (e.g., on return from a branch) gets fresh inputs. + currentStackFrame.reported = {}; + + var primitiveReportedValue = null; + primitiveReportedValue = blockFunction(argValues, { + stackFrame: currentStackFrame.executionContext, + target: target, + yield: function () { + thread.status = Thread.STATUS_YIELD; + }, + startBranch: function (branchNum, isLoop) { + sequencer.stepToBranch(thread, branchNum, isLoop); + }, + stopAll: function () { + runtime.stopAll(); + }, + stopOtherTargetThreads: function () { + runtime.stopForTarget(target, thread); + }, + stopThread: function () { + sequencer.retireThread(thread); + }, + startProcedure: function (procedureCode) { + sequencer.stepToProcedure(thread, procedureCode); + }, + getProcedureParamNames: function (procedureCode) { + return blockContainer.getProcedureParamNames(procedureCode); + }, + pushParam: function (paramName, paramValue) { + thread.pushParam(paramName, paramValue); + }, + getParam: function (paramName) { + return thread.getParam(paramName); + }, + startHats: function (requestedHat, optMatchFields, optTarget) { + return ( + runtime.startHats(requestedHat, optMatchFields, optTarget) + ); + }, + ioQuery: function (device, func, args) { + // Find the I/O device and execute the query/function call. + if (runtime.ioDevices[device] && runtime.ioDevices[device][func]) { + var devObject = runtime.ioDevices[device]; + // @todo Figure out why eslint complains about no-useless-call + // no-useless-call can't tell if the call is useless for dynamic + // expressions... or something. Not exactly sure why it + // complains here. + // eslint-disable-next-line no-useless-call + return devObject[func].call(devObject, args); + } + } + }); + + if (typeof primitiveReportedValue === 'undefined') { + // No value reported - potentially a command block. + // Edge-activated hats don't request a glow; all commands do. + thread.requestScriptGlowInFrame = true; + } + + // If it's a promise, wait until promise resolves. + if (isPromise(primitiveReportedValue)) { + if (thread.status === Thread.STATUS_RUNNING) { + // Primitive returned a promise; automatically yield thread. + thread.status = Thread.STATUS_PROMISE_WAIT; + } + // Promise handlers + primitiveReportedValue.then(function (resolvedValue) { + handleReport(resolvedValue); + if (typeof resolvedValue === 'undefined') { + var popped = thread.popStack(); + var nextBlockId = thread.target.blocks.getNextBlock(popped); + thread.pushStack(nextBlockId); + } else { + thread.popStack(); + } + }, function (rejectionReason) { + // Promise rejected: the primitive had some error. + // Log it and proceed. + log.warn('Primitive rejected promise: ', rejectionReason); + thread.status = Thread.STATUS_RUNNING; + thread.popStack(); + }); + } else if (thread.status === Thread.STATUS_RUNNING) { + handleReport(primitiveReportedValue); + } + }; + + module.exports = execute; + + +/***/ }, +/* 11 */ +/***/ function(module, exports, __webpack_require__) { + + var minilog = __webpack_require__(12); + minilog.enable(); + + module.exports = minilog('vm'); + + +/***/ }, +/* 12 */ +/***/ function(module, exports, __webpack_require__) { + + var Minilog = __webpack_require__(13); + + var oldEnable = Minilog.enable, + oldDisable = Minilog.disable, + isChrome = (typeof navigator != 'undefined' && /chrome/i.test(navigator.userAgent)), + console = __webpack_require__(17); + + // Use a more capable logging backend if on Chrome + Minilog.defaultBackend = (isChrome ? console.minilog : console); + + // apply enable inputs from localStorage and from the URL + if(typeof window != 'undefined') { + try { + Minilog.enable(JSON.parse(window.localStorage['minilogSettings'])); + } catch(e) {} + if(window.location && window.location.search) { + var match = RegExp('[?&]minilog=([^&]*)').exec(window.location.search); + match && Minilog.enable(decodeURIComponent(match[1])); + } + } + + // Make enable also add to localStorage + Minilog.enable = function() { + oldEnable.call(Minilog, true); + try { window.localStorage['minilogSettings'] = JSON.stringify(true); } catch(e) {} + return this; + }; + + Minilog.disable = function() { + oldDisable.call(Minilog); + try { delete window.localStorage.minilogSettings; } catch(e) {} + return this; + }; + + exports = module.exports = Minilog; + + exports.backends = { + array: __webpack_require__(21), + browser: Minilog.defaultBackend, + localStorage: __webpack_require__(22), + jQuery: __webpack_require__(23) + }; + + +/***/ }, +/* 13 */ +/***/ function(module, exports, __webpack_require__) { + + var Transform = __webpack_require__(14), + Filter = __webpack_require__(16); + + var log = new Transform(), + slice = Array.prototype.slice; + + exports = module.exports = function create(name) { + var o = function() { log.write(name, undefined, slice.call(arguments)); return o; }; + o.debug = function() { log.write(name, 'debug', slice.call(arguments)); return o; }; + o.info = function() { log.write(name, 'info', slice.call(arguments)); return o; }; + o.warn = function() { log.write(name, 'warn', slice.call(arguments)); return o; }; + o.error = function() { log.write(name, 'error', slice.call(arguments)); return o; }; + o.log = o.debug; // for interface compliance with Node and browser consoles + o.suggest = exports.suggest; + o.format = log.format; + return o; + }; + + // filled in separately + exports.defaultBackend = exports.defaultFormatter = null; + + exports.pipe = function(dest) { + return log.pipe(dest); + }; + + exports.end = exports.unpipe = exports.disable = function(from) { + return log.unpipe(from); + }; + + exports.Transform = Transform; + exports.Filter = Filter; + // this is the default filter that's applied when .enable() is called normally + // you can bypass it completely and set up your own pipes + exports.suggest = new Filter(); + + exports.enable = function() { + if(exports.defaultFormatter) { + return log.pipe(exports.suggest) // filter + .pipe(exports.defaultFormatter) // formatter + .pipe(exports.defaultBackend); // backend + } + return log.pipe(exports.suggest) // filter + .pipe(exports.defaultBackend); // formatter + }; + + + +/***/ }, +/* 14 */ +/***/ function(module, exports, __webpack_require__) { + + var microee = __webpack_require__(15); + + // Implements a subset of Node's stream.Transform - in a cross-platform manner. + function Transform() {} + + microee.mixin(Transform); + + // The write() signature is different from Node's + // --> makes it much easier to work with objects in logs. + // One of the lessons from v1 was that it's better to target + // a good browser rather than the lowest common denominator + // internally. + // If you want to use external streams, pipe() to ./stringify.js first. + Transform.prototype.write = function(name, level, args) { + this.emit('item', name, level, args); + }; + + Transform.prototype.end = function() { + this.emit('end'); + this.removeAllListeners(); + }; + + Transform.prototype.pipe = function(dest) { + var s = this; + // prevent double piping + s.emit('unpipe', dest); + // tell the dest that it's being piped to + dest.emit('pipe', s); + + function onItem() { + dest.write.apply(dest, Array.prototype.slice.call(arguments)); + } + function onEnd() { !dest._isStdio && dest.end(); } + + s.on('item', onItem); + s.on('end', onEnd); + + s.when('unpipe', function(from) { + var match = (from === dest) || typeof from == 'undefined'; + if(match) { + s.removeListener('item', onItem); + s.removeListener('end', onEnd); + dest.emit('unpipe'); + } + return match; + }); + + return dest; + }; + + Transform.prototype.unpipe = function(from) { + this.emit('unpipe', from); + return this; + }; + + Transform.prototype.format = function(dest) { + throw new Error([ + 'Warning: .format() is deprecated in Minilog v2! Use .pipe() instead. For example:', + 'var Minilog = require(\'minilog\');', + 'Minilog', + ' .pipe(Minilog.backends.console.formatClean)', + ' .pipe(Minilog.backends.console);'].join('\n')); + }; + + Transform.mixin = function(dest) { + var o = Transform.prototype, k; + for (k in o) { + o.hasOwnProperty(k) && (dest.prototype[k] = o[k]); + } + }; + + module.exports = Transform; + + +/***/ }, +/* 15 */ +/***/ function(module, exports) { + + function M() { this._events = {}; } + M.prototype = { + on: function(ev, cb) { + this._events || (this._events = {}); + var e = this._events; + (e[ev] || (e[ev] = [])).push(cb); + return this; + }, + removeListener: function(ev, cb) { + var e = this._events[ev] || [], i; + for(i = e.length-1; i >= 0 && e[i]; i--){ + if(e[i] === cb || e[i].cb === cb) { e.splice(i, 1); } + } + }, + removeAllListeners: function(ev) { + if(!ev) { this._events = {}; } + else { this._events[ev] && (this._events[ev] = []); } + }, + emit: function(ev) { + this._events || (this._events = {}); + var args = Array.prototype.slice.call(arguments, 1), i, e = this._events[ev] || []; + for(i = e.length-1; i >= 0 && e[i]; i--){ + e[i].apply(this, args); + } + return this; + }, + when: function(ev, cb) { + return this.once(ev, cb, true); + }, + once: function(ev, cb, when) { + if(!cb) return this; + function c() { + if(!when) this.removeListener(ev, c); + if(cb.apply(this, arguments) && when) this.removeListener(ev, c); + } + c.cb = cb; + this.on(ev, c); + return this; + } + }; + M.mixin = function(dest) { + var o = M.prototype, k; + for (k in o) { + o.hasOwnProperty(k) && (dest.prototype[k] = o[k]); + } + }; + module.exports = M; + + +/***/ }, +/* 16 */ +/***/ function(module, exports, __webpack_require__) { + + // default filter + var Transform = __webpack_require__(14); + + var levelMap = { debug: 1, info: 2, warn: 3, error: 4 }; + + function Filter() { + this.enabled = true; + this.defaultResult = true; + this.clear(); + } + + Transform.mixin(Filter); + + // allow all matching, with level >= given level + Filter.prototype.allow = function(name, level) { + this._white.push({ n: name, l: levelMap[level] }); + return this; + }; + + // deny all matching, with level <= given level + Filter.prototype.deny = function(name, level) { + this._black.push({ n: name, l: levelMap[level] }); + return this; + }; + + Filter.prototype.clear = function() { + this._white = []; + this._black = []; + return this; + }; + + function test(rule, name) { + // use .test for RegExps + return (rule.n.test ? rule.n.test(name) : rule.n == name); + }; + + Filter.prototype.test = function(name, level) { + var i, len = Math.max(this._white.length, this._black.length); + for(i = 0; i < len; i++) { + if(this._white[i] && test(this._white[i], name) && levelMap[level] >= this._white[i].l) { + return true; + } + if(this._black[i] && test(this._black[i], name) && levelMap[level] < this._black[i].l) { + return false; + } + } + return this.defaultResult; + }; + + Filter.prototype.write = function(name, level, args) { + if(!this.enabled || this.test(name, level)) { + return this.emit('item', name, level, args); + } + }; + + module.exports = Filter; + + +/***/ }, +/* 17 */ +/***/ function(module, exports, __webpack_require__) { + + var Transform = __webpack_require__(14); + + var newlines = /\n+$/, + logger = new Transform(); + + logger.write = function(name, level, args) { + var i = args.length-1; + if (typeof console === 'undefined' || !console.log) { + return; + } + if(console.log.apply) { + return console.log.apply(console, [name, level].concat(args)); + } else if(JSON && JSON.stringify) { + // console.log.apply is undefined in IE8 and IE9 + // for IE8/9: make console.log at least a bit less awful + if(args[i] && typeof args[i] == 'string') { + args[i] = args[i].replace(newlines, ''); + } + try { + for(i = 0; i < args.length; i++) { + args[i] = JSON.stringify(args[i]); + } + } catch(e) {} + console.log(args.join(' ')); + } + }; + + logger.formatters = ['color', 'minilog']; + logger.color = __webpack_require__(18); + logger.minilog = __webpack_require__(20); + + module.exports = logger; + + +/***/ }, +/* 18 */ +/***/ function(module, exports, __webpack_require__) { + + var Transform = __webpack_require__(14), + color = __webpack_require__(19); + + var colors = { debug: ['cyan'], info: ['purple' ], warn: [ 'yellow', true ], error: [ 'red', true ] }, + logger = new Transform(); + + logger.write = function(name, level, args) { + var fn = console.log; + if(console[level] && console[level].apply) { + fn = console[level]; + fn.apply(console, [ '%c'+name+' %c'+level, color('gray'), color.apply(color, colors[level])].concat(args)); + } + }; + + // NOP, because piping the formatted logs can only cause trouble. + logger.pipe = function() { }; + + module.exports = logger; + + +/***/ }, +/* 19 */ +/***/ function(module, exports) { + + var hex = { + black: '#000', + red: '#c23621', + green: '#25bc26', + yellow: '#bbbb00', + blue: '#492ee1', + magenta: '#d338d3', + cyan: '#33bbc8', + gray: '#808080', + purple: '#708' + }; + function color(fg, isInverse) { + if(isInverse) { + return 'color: #fff; background: '+hex[fg]+';'; + } else { + return 'color: '+hex[fg]+';'; + } + } + + module.exports = color; + + +/***/ }, +/* 20 */ +/***/ function(module, exports, __webpack_require__) { + + var Transform = __webpack_require__(14), + color = __webpack_require__(19), + colors = { debug: ['gray'], info: ['purple' ], warn: [ 'yellow', true ], error: [ 'red', true ] }, + logger = new Transform(); + + logger.write = function(name, level, args) { + var fn = console.log; + if(level != 'debug' && console[level]) { + fn = console[level]; + } + + var subset = [], i = 0; + if(level != 'info') { + for(; i < args.length; i++) { + if(typeof args[i] != 'string') break; + } + fn.apply(console, [ '%c'+name +' '+ args.slice(0, i).join(' '), color.apply(color, colors[level]) ].concat(args.slice(i))); + } else { + fn.apply(console, [ '%c'+name, color.apply(color, colors[level]) ].concat(args)); + } + }; + + // NOP, because piping the formatted logs can only cause trouble. + logger.pipe = function() { }; + + module.exports = logger; + + +/***/ }, +/* 21 */ +/***/ function(module, exports, __webpack_require__) { + + var Transform = __webpack_require__(14), + cache = [ ]; + + var logger = new Transform(); + + logger.write = function(name, level, args) { + cache.push([ name, level, args ]); + }; + + // utility functions + logger.get = function() { return cache; }; + logger.empty = function() { cache = []; }; + + module.exports = logger; + + +/***/ }, +/* 22 */ +/***/ function(module, exports, __webpack_require__) { + + var Transform = __webpack_require__(14), + cache = false; + + var logger = new Transform(); + + logger.write = function(name, level, args) { + if(typeof window == 'undefined' || typeof JSON == 'undefined' || !JSON.stringify || !JSON.parse) return; + try { + if(!cache) { cache = (window.localStorage.minilog ? JSON.parse(window.localStorage.minilog) : []); } + cache.push([ new Date().toString(), name, level, args ]); + window.localStorage.minilog = JSON.stringify(cache); + } catch(e) {} + }; + + module.exports = logger; + +/***/ }, +/* 23 */ +/***/ function(module, exports, __webpack_require__) { + + var Transform = __webpack_require__(14); + + var cid = new Date().valueOf().toString(36); + + function AjaxLogger(options) { + this.url = options.url || ''; + this.cache = []; + this.timer = null; + this.interval = options.interval || 30*1000; + this.enabled = true; + this.jQuery = window.jQuery; + this.extras = {}; + } + + Transform.mixin(AjaxLogger); + + AjaxLogger.prototype.write = function(name, level, args) { + if(!this.timer) { this.init(); } + this.cache.push([name, level].concat(args)); + }; + + AjaxLogger.prototype.init = function() { + if(!this.enabled || !this.jQuery) return; + var self = this; + this.timer = setTimeout(function() { + var i, logs = [], ajaxData, url = self.url; + if(self.cache.length == 0) return self.init(); + // Test each log line and only log the ones that are valid (e.g. don't have circular references). + // Slight performance hit but benefit is we log all valid lines. + for(i = 0; i < self.cache.length; i++) { + try { + JSON.stringify(self.cache[i]); + logs.push(self.cache[i]); + } catch(e) { } + } + if(self.jQuery.isEmptyObject(self.extras)) { + ajaxData = JSON.stringify({ logs: logs }); + url = self.url + '?client_id=' + cid; + } else { + ajaxData = JSON.stringify(self.jQuery.extend({logs: logs}, self.extras)); + } + + self.jQuery.ajax(url, { + type: 'POST', + cache: false, + processData: false, + data: ajaxData, + contentType: 'application/json', + timeout: 10000 + }).success(function(data, status, jqxhr) { + if(data.interval) { + self.interval = Math.max(1000, data.interval); + } + }).error(function() { + self.interval = 30000; + }).always(function() { + self.init(); + }); + self.cache = []; + }, this.interval); + }; + + AjaxLogger.prototype.end = function() {}; + + // wait until jQuery is defined. Useful if you don't control the load order. + AjaxLogger.jQueryWait = function(onDone) { + if(typeof window !== 'undefined' && (window.jQuery || window.$)) { + return onDone(window.jQuery || window.$); + } else if (typeof window !== 'undefined') { + setTimeout(function() { AjaxLogger.jQueryWait(onDone); }, 200); + } + }; + + module.exports = AjaxLogger; + + +/***/ }, +/* 24 */ +/***/ function(module, exports, __webpack_require__) { + + var adapter = __webpack_require__(25); + var mutationAdapter = __webpack_require__(26); + var xmlEscape = __webpack_require__(78); + + /** + * @fileoverview + * Store and mutate the VM block representation, + * and handle updates from Scratch Blocks events. + */ + + var Blocks = function () { + /** + * All blocks in the workspace. + * Keys are block IDs, values are metadata about the block. + * @type {Object.} + */ + this._blocks = {}; + + /** + * All top-level scripts in the workspace. + * A list of block IDs that represent scripts (i.e., first block in script). + * @type {Array.} + */ + this._scripts = []; + }; + + /** + * Blockly inputs that represent statements/branch. + * are prefixed with this string. + * @const{string} + */ + Blocks.BRANCH_INPUT_PREFIX = 'SUBSTACK'; + + /** + * Provide an object with metadata for the requested block ID. + * @param {!string} blockId ID of block we have stored. + * @return {?Object} Metadata about the block, if it exists. + */ + Blocks.prototype.getBlock = function (blockId) { + return this._blocks[blockId]; + }; + + /** + * Get all known top-level blocks that start scripts. + * @return {Array.} List of block IDs. + */ + Blocks.prototype.getScripts = function () { + return this._scripts; + }; + + /** + * Get the next block for a particular block + * @param {?string} id ID of block to get the next block for + * @return {?string} ID of next block in the sequence + */ + Blocks.prototype.getNextBlock = function (id) { + if (typeof this._blocks[id] === 'undefined') return null; + return this._blocks[id].next; + }; + + /** + * Get the branch for a particular C-shaped block. + * @param {?string} id ID for block to get the branch for. + * @param {?number} branchNum Which branch to select (e.g. for if-else). + * @return {?string} ID of block in the branch. + */ + Blocks.prototype.getBranch = function (id, branchNum) { + var block = this._blocks[id]; + if (typeof block === 'undefined') return null; + if (!branchNum) branchNum = 1; + + var inputName = Blocks.BRANCH_INPUT_PREFIX; + if (branchNum > 1) { + inputName += branchNum; + } + + // Empty C-block? + if (!(inputName in block.inputs)) return null; + return block.inputs[inputName].block; + }; + + /** + * Get the opcode for a particular block + * @param {?string} id ID of block to query + * @return {?string} the opcode corresponding to that block + */ + Blocks.prototype.getOpcode = function (id) { + if (typeof this._blocks[id] === 'undefined') return null; + return this._blocks[id].opcode; + }; + + /** + * Get all fields and their values for a block. + * @param {?string} id ID of block to query. + * @return {!Object} All fields and their values. + */ + Blocks.prototype.getFields = function (id) { + if (typeof this._blocks[id] === 'undefined') return null; + return this._blocks[id].fields; + }; + + /** + * Get all non-branch inputs for a block. + * @param {?string} id ID of block to query. + * @return {!Object} All non-branch inputs and their associated blocks. + */ + Blocks.prototype.getInputs = function (id) { + if (typeof this._blocks[id] === 'undefined') return null; + var inputs = {}; + for (var input in this._blocks[id].inputs) { + // Ignore blocks prefixed with branch prefix. + if (input.substring(0, Blocks.BRANCH_INPUT_PREFIX.length) !== + Blocks.BRANCH_INPUT_PREFIX) { + inputs[input] = this._blocks[id].inputs[input]; + } + } + return inputs; + }; + + /** + * Get mutation data for a block. + * @param {?string} id ID of block to query. + * @return {!Object} Mutation for the block. + */ + Blocks.prototype.getMutation = function (id) { + if (typeof this._blocks[id] === 'undefined') return null; + return this._blocks[id].mutation; + }; + + /** + * Get the top-level script for a given block. + * @param {?string} id ID of block to query. + * @return {?string} ID of top-level script block. + */ + Blocks.prototype.getTopLevelScript = function (id) { + if (typeof this._blocks[id] === 'undefined') return null; + var block = this._blocks[id]; + while (block.parent !== null) { + block = this._blocks[block.parent]; + } + return block.id; + }; + + /** + * Get the procedure definition for a given name. + * @param {?string} name Name of procedure to query. + * @return {?string} ID of procedure definition. + */ + Blocks.prototype.getProcedureDefinition = function (name) { + for (var id in this._blocks) { + var block = this._blocks[id]; + if ((block.opcode === 'procedures_defnoreturn' || + block.opcode === 'procedures_defreturn') && + block.mutation.proccode === name) { + return id; + } + } + return null; + }; + + /** + * Get the procedure definition for a given name. + * @param {?string} name Name of procedure to query. + * @return {?string} ID of procedure definition. + */ + Blocks.prototype.getProcedureParamNames = function (name) { + for (var id in this._blocks) { + var block = this._blocks[id]; + if ((block.opcode === 'procedures_defnoreturn' || + block.opcode === 'procedures_defreturn') && + block.mutation.proccode === name) { + return JSON.parse(block.mutation.argumentnames); + } + } + return null; + }; + + // --------------------------------------------------------------------- + + /** + * Create event listener for blocks. Handles validation and serves as a generic + * adapter between the blocks and the runtime interface. + * @param {Object} e Blockly "block" event + * @param {?Runtime} optRuntime Optional runtime to forward click events to. + */ + + Blocks.prototype.blocklyListen = function (e, optRuntime) { + // Validate event + if (typeof e !== 'object') return; + if (typeof e.blockId !== 'string') return; + + // UI event: clicked scripts toggle in the runtime. + if (e.element === 'stackclick') { + if (optRuntime) { + optRuntime.toggleScript(e.blockId); + } + return; + } + + // Block create/update/destroy + switch (e.type) { + case 'create': + var newBlocks = adapter(e); + // A create event can create many blocks. Add them all. + for (var i = 0; i < newBlocks.length; i++) { + this.createBlock(newBlocks[i]); + } + break; + case 'change': + this.changeBlock({ + id: e.blockId, + element: e.element, + name: e.name, + value: e.newValue + }); + break; + case 'move': + this.moveBlock({ + id: e.blockId, + oldParent: e.oldParentId, + oldInput: e.oldInputName, + newParent: e.newParentId, + newInput: e.newInputName, + newCoordinate: e.newCoordinate + }); + break; + case 'delete': + // Don't accept delete events for missing blocks, + // or shadow blocks being obscured. + if (!this._blocks.hasOwnProperty(e.blockId) || + this._blocks[e.blockId].shadow) { + return; + } + // Inform any runtime to forget about glows on this script. + if (optRuntime && this._blocks[e.blockId].topLevel) { + optRuntime.quietGlow(e.blockId); + } + this.deleteBlock({ + id: e.blockId + }); + break; + } + }; + + // --------------------------------------------------------------------- + + /** + * Block management: create blocks and scripts from a `create` event + * @param {!Object} block Blockly create event to be processed + */ + Blocks.prototype.createBlock = function (block) { + // Does the block already exist? + // Could happen, e.g., for an unobscured shadow. + if (this._blocks.hasOwnProperty(block.id)) { + return; + } + // Create new block. + this._blocks[block.id] = block; + // Push block id to scripts array. + // Blocks are added as a top-level stack if they are marked as a top-block + // (if they were top-level XML in the event). + if (block.topLevel) { + this._addScript(block.id); + } + }; + + /** + * Block management: change block field values + * @param {!Object} args Blockly change event to be processed + */ + Blocks.prototype.changeBlock = function (args) { + // Validate + if (args.element !== 'field' && args.element !== 'mutation') return; + if (typeof this._blocks[args.id] === 'undefined') return; + + if (args.element === 'field') { + // Update block value + if (!this._blocks[args.id].fields[args.name]) return; + this._blocks[args.id].fields[args.name].value = args.value; + } else if (args.element === 'mutation') { + this._blocks[args.id].mutation = mutationAdapter(args.value); + } + }; + + /** + * Block management: move blocks from parent to parent + * @param {!Object} e Blockly move event to be processed + */ + Blocks.prototype.moveBlock = function (e) { + if (!this._blocks.hasOwnProperty(e.id)) { + return; + } + + // Move coordinate changes. + if (e.newCoordinate) { + this._blocks[e.id].x = e.newCoordinate.x; + this._blocks[e.id].y = e.newCoordinate.y; + } + + // Remove from any old parent. + if (typeof e.oldParent !== 'undefined') { + var oldParent = this._blocks[e.oldParent]; + if (typeof e.oldInput !== 'undefined' && + oldParent.inputs[e.oldInput].block === e.id) { + // This block was connected to the old parent's input. + oldParent.inputs[e.oldInput].block = null; + } else if (oldParent.next === e.id) { + // This block was connected to the old parent's next connection. + oldParent.next = null; + } + this._blocks[e.id].parent = null; + } + + // Has the block become a top-level block? + if (typeof e.newParent === 'undefined') { + this._addScript(e.id); + } else { + // Remove script, if one exists. + this._deleteScript(e.id); + // Otherwise, try to connect it in its new place. + if (typeof e.newInput === 'undefined') { + // Moved to the new parent's next connection. + this._blocks[e.newParent].next = e.id; + } else { + // Moved to the new parent's input. + // Don't obscure the shadow block. + var oldShadow = null; + if (this._blocks[e.newParent].inputs.hasOwnProperty(e.newInput)) { + oldShadow = this._blocks[e.newParent].inputs[e.newInput].shadow; + } + this._blocks[e.newParent].inputs[e.newInput] = { + name: e.newInput, + block: e.id, + shadow: oldShadow + }; + } + this._blocks[e.id].parent = e.newParent; + } + }; + + /** + * Block management: delete blocks and their associated scripts. + * @param {!Object} e Blockly delete event to be processed. + */ + Blocks.prototype.deleteBlock = function (e) { + // @todo In runtime, stop threads running on this script. + + // Get block + var block = this._blocks[e.id]; + + // Delete children + if (block.next !== null) { + this.deleteBlock({id: block.next}); + } + + // Delete inputs (including branches) + for (var input in block.inputs) { + // If it's null, the block in this input moved away. + if (block.inputs[input].block !== null) { + this.deleteBlock({id: block.inputs[input].block}); + } + // Delete obscured shadow blocks. + if (block.inputs[input].shadow !== null && + block.inputs[input].shadow !== block.inputs[input].block) { + this.deleteBlock({id: block.inputs[input].shadow}); + } + } + + // Delete any script starting with this block. + this._deleteScript(e.id); + + // Delete block itself. + delete this._blocks[e.id]; + }; + + // --------------------------------------------------------------------- + + /** + * Encode all of `this._blocks` as an XML string usable + * by a Blockly/scratch-blocks workspace. + * @return {string} String of XML representing this object's blocks. + */ + Blocks.prototype.toXML = function () { + var xmlString = ''; + for (var i = 0; i < this._scripts.length; i++) { + xmlString += this.blockToXML(this._scripts[i]); + } + return xmlString + ''; + }; + + /** + * Recursively encode an individual block and its children + * into a Blockly/scratch-blocks XML string. + * @param {!string} blockId ID of block to encode. + * @return {string} String of XML representing this block and any children. + */ + Blocks.prototype.blockToXML = function (blockId) { + var block = this._blocks[blockId]; + // Encode properties of this block. + var tagName = (block.shadow) ? 'shadow' : 'block'; + var xy = (block.topLevel) ? + ' x="' + block.x + '" y="' + block.y + '"' : + ''; + var xmlString = ''; + xmlString += '<' + tagName + + ' id="' + block.id + '"' + + ' type="' + block.opcode + '"' + + xy + + '>'; + // Add any mutation. Must come before inputs. + if (block.mutation) { + xmlString += this.mutationToXML(block.mutation); + } + // Add any inputs on this block. + for (var input in block.inputs) { + var blockInput = block.inputs[input]; + // Only encode a value tag if the value input is occupied. + if (blockInput.block || blockInput.shadow) { + xmlString += ''; + if (blockInput.block) { + xmlString += this.blockToXML(blockInput.block); + } + if (blockInput.shadow && blockInput.shadow !== blockInput.block) { + // Obscured shadow. + xmlString += this.blockToXML(blockInput.shadow); + } + xmlString += ''; + } + } + // Add any fields on this block. + for (var field in block.fields) { + var blockField = block.fields[field]; + var value = blockField.value; + if (typeof value === 'string') { + value = xmlEscape(blockField.value); + } + xmlString += '' + + value + ''; + } + // Add blocks connected to the next connection. + if (block.next) { + xmlString += '' + this.blockToXML(block.next) + ''; + } + xmlString += ''; + return xmlString; + }; + + /** + * Recursively encode a mutation object to XML. + * @param {!Object} mutation Object representing a mutation. + * @return {string} XML string representing a mutation. + */ + Blocks.prototype.mutationToXML = function (mutation) { + var mutationString = '<' + mutation.tagName; + for (var prop in mutation) { + if (prop === 'children' || prop === 'tagName') continue; + var mutationValue = (typeof mutation[prop] === 'string') ? + xmlEscape(mutation[prop]) : mutation[prop]; + mutationString += ' ' + prop + '="' + mutationValue + '"'; + } + mutationString += '>'; + for (var i = 0; i < mutation.children.length; i++) { + mutationString += this.mutationToXML(mutation.children[i]); + } + mutationString += ''; + return mutationString; + }; + + // --------------------------------------------------------------------- + + /** + * Helper to add a stack to `this._scripts`. + * @param {?string} topBlockId ID of block that starts the script. + */ + Blocks.prototype._addScript = function (topBlockId) { + var i = this._scripts.indexOf(topBlockId); + if (i > -1) return; // Already in scripts. + this._scripts.push(topBlockId); + // Update `topLevel` property on the top block. + this._blocks[topBlockId].topLevel = true; + }; + + /** + * Helper to remove a script from `this._scripts`. + * @param {?string} topBlockId ID of block that starts the script. + */ + Blocks.prototype._deleteScript = function (topBlockId) { + var i = this._scripts.indexOf(topBlockId); + if (i > -1) this._scripts.splice(i, 1); + // Update `topLevel` property on the top block. + if (this._blocks[topBlockId]) this._blocks[topBlockId].topLevel = false; + }; + + module.exports = Blocks; + + +/***/ }, +/* 25 */ +/***/ function(module, exports, __webpack_require__) { + + var mutationAdapter = __webpack_require__(26); + var html = __webpack_require__(27); + + /** + * Convert outer blocks DOM from a Blockly CREATE event + * to a usable form for the Scratch runtime. + * This structure is based on Blockly xml.js:`domToWorkspace` and `domToBlock`. + * @param {Element} blocksDOM DOM tree for this event. + * @return {Array.} Usable list of blocks from this CREATE event. + */ + var domToBlocks = function (blocksDOM) { + // At this level, there could be multiple blocks adjacent in the DOM tree. + var blocks = {}; + for (var i = 0; i < blocksDOM.length; i++) { + var block = blocksDOM[i]; + if (!block.name || !block.attribs) { + continue; + } + var tagName = block.name.toLowerCase(); + if (tagName === 'block' || tagName === 'shadow') { + domToBlock(block, blocks, true, null); + } + } + // Flatten blocks object into a list. + var blocksList = []; + for (var b in blocks) { + blocksList.push(blocks[b]); + } + return blocksList; + }; + + /** + * Adapter between block creation events and block representation which can be + * used by the Scratch runtime. + * @param {Object} e `Blockly.events.create` + * @return {Array.} List of blocks from this CREATE event. + */ + var adapter = function (e) { + // Validate input + if (typeof e !== 'object') return; + if (typeof e.xml !== 'object') return; + + return domToBlocks(html.parseDOM(e.xml.outerHTML)); + }; + + /** + * Convert and an individual block DOM to the representation tree. + * Based on Blockly's `domToBlockHeadless_`. + * @param {Element} blockDOM DOM tree for an individual block. + * @param {Object} blocks Collection of blocks to add to. + * @param {Boolean} isTopBlock Whether blocks at this level are "top blocks." + * @param {?string} parent Parent block ID. + * @return {undefined} + */ + var domToBlock = function (blockDOM, blocks, isTopBlock, parent) { + // Block skeleton. + var block = { + id: blockDOM.attribs.id, // Block ID + opcode: blockDOM.attribs.type, // For execution, "event_whengreenflag". + inputs: {}, // Inputs to this block and the blocks they point to. + fields: {}, // Fields on this block and their values. + next: null, // Next block in the stack, if one exists. + topLevel: isTopBlock, // If this block starts a stack. + parent: parent, // Parent block ID, if available. + shadow: blockDOM.name === 'shadow', // If this represents a shadow/slot. + x: blockDOM.attribs.x, // X position of script, if top-level. + y: blockDOM.attribs.y // Y position of script, if top-level. + }; + + // Add the block to the representation tree. + blocks[block.id] = block; + + // Process XML children and find enclosed blocks, fields, etc. + for (var i = 0; i < blockDOM.children.length; i++) { + var xmlChild = blockDOM.children[i]; + // Enclosed blocks and shadows + var childBlockNode = null; + var childShadowNode = null; + for (var j = 0; j < xmlChild.children.length; j++) { + var grandChildNode = xmlChild.children[j]; + if (!grandChildNode.name) { + // Non-XML tag node. + continue; + } + var grandChildNodeName = grandChildNode.name.toLowerCase(); + if (grandChildNodeName === 'block') { + childBlockNode = grandChildNode; + } else if (grandChildNodeName === 'shadow') { + childShadowNode = grandChildNode; + } + } + + // Use shadow block only if there's no real block node. + if (!childBlockNode && childShadowNode) { + childBlockNode = childShadowNode; + } + + // Not all Blockly-type blocks are handled here, + // as we won't be using all of them for Scratch. + switch (xmlChild.name.toLowerCase()) { + case 'field': + // Add the field to this block. + var fieldName = xmlChild.attribs.name; + var fieldData = ''; + if (xmlChild.children.length > 0 && xmlChild.children[0].data) { + fieldData = xmlChild.children[0].data; + } else { + // If the child of the field with a data property + // doesn't exist, set the data to an empty string. + fieldData = ''; + } + block.fields[fieldName] = { + name: fieldName, + value: fieldData + }; + break; + case 'value': + case 'statement': + // Recursively generate block structure for input block. + domToBlock(childBlockNode, blocks, false, block.id); + if (childShadowNode && childBlockNode !== childShadowNode) { + // Also generate the shadow block. + domToBlock(childShadowNode, blocks, false, block.id); + } + // Link this block's input to the child block. + var inputName = xmlChild.attribs.name; + block.inputs[inputName] = { + name: inputName, + block: childBlockNode.attribs.id, + shadow: childShadowNode ? childShadowNode.attribs.id : null + }; + break; + case 'next': + if (!childBlockNode || !childBlockNode.attribs) { + // Invalid child block. + continue; + } + // Recursively generate block structure for next block. + domToBlock(childBlockNode, blocks, false, block.id); + // Link next block to this block. + block.next = childBlockNode.attribs.id; + break; + case 'mutation': + block.mutation = mutationAdapter(xmlChild); + break; + } + } + }; + + module.exports = adapter; + + +/***/ }, +/* 26 */ +/***/ function(module, exports, __webpack_require__) { + + var html = __webpack_require__(27); + + /** + * Convert a part of a mutation DOM to a mutation VM object, recursively. + * @param {Object} dom DOM object for mutation tag. + * @return {Object} Object representing useful parts of this mutation. + */ + var mutatorTagToObject = function (dom) { + var obj = Object.create(null); + obj.tagName = dom.name; + obj.children = []; + for (var prop in dom.attribs) { + if (prop === 'xmlns') continue; + obj[prop] = dom.attribs[prop]; + } + for (var i = 0; i < dom.children.length; i++) { + obj.children.push( + mutatorTagToObject(dom.children[i]) + ); + } + return obj; + }; + + /** + * Adapter between mutator XML or DOM and block representation which can be + * used by the Scratch runtime. + * @param {(Object|string)} mutation Mutation XML string or DOM. + * @return {Object} Object representing the mutation. + */ + var mutationAdpater = function (mutation) { + var mutationParsed; + // Check if the mutation is already parsed; if not, parse it. + if (typeof mutation === 'object') { + mutationParsed = mutation; + } else { + mutationParsed = html.parseDOM(mutation)[0]; + } + return mutatorTagToObject(mutationParsed); + }; + + module.exports = mutationAdpater; + + +/***/ }, +/* 27 */ +/***/ function(module, exports, __webpack_require__) { + + var Parser = __webpack_require__(28), + DomHandler = __webpack_require__(35); + + function defineProp(name, value){ + delete module.exports[name]; + module.exports[name] = value; + return value; + } + + module.exports = { + Parser: Parser, + Tokenizer: __webpack_require__(29), + ElementType: __webpack_require__(36), + DomHandler: DomHandler, + get FeedHandler(){ + return defineProp("FeedHandler", __webpack_require__(39)); + }, + get Stream(){ + return defineProp("Stream", __webpack_require__(40)); + }, + get WritableStream(){ + return defineProp("WritableStream", __webpack_require__(41)); + }, + get ProxyHandler(){ + return defineProp("ProxyHandler", __webpack_require__(64)); + }, + get DomUtils(){ + return defineProp("DomUtils", __webpack_require__(65)); + }, + get CollectingHandler(){ + return defineProp("CollectingHandler", __webpack_require__(77)); + }, + // For legacy support + DefaultHandler: DomHandler, + get RssHandler(){ + return defineProp("RssHandler", this.FeedHandler); + }, + //helper methods + parseDOM: function(data, options){ + var handler = new DomHandler(options); + new Parser(handler, options).end(data); + return handler.dom; + }, + parseFeed: function(feed, options){ + var handler = new module.exports.FeedHandler(options); + new Parser(handler, options).end(feed); + return handler.dom; + }, + createDomStream: function(cb, options, elementCb){ + var handler = new DomHandler(cb, options, elementCb); + return new Parser(handler, options); + }, + // List of all events that the parser emits + EVENTS: { /* Format: eventname: number of arguments */ + attribute: 2, + cdatastart: 0, + cdataend: 0, + text: 1, + processinginstruction: 2, + comment: 1, + commentend: 0, + closetag: 1, + opentag: 2, + opentagname: 1, + error: 1, + end: 0 + } + }; + + +/***/ }, +/* 28 */ +/***/ function(module, exports, __webpack_require__) { + + var Tokenizer = __webpack_require__(29); + + /* + Options: + + xmlMode: Disables the special behavior for script/style tags (false by default) + lowerCaseAttributeNames: call .toLowerCase for each attribute name (true if xmlMode is `false`) + lowerCaseTags: call .toLowerCase for each tag name (true if xmlMode is `false`) + */ + + /* + Callbacks: + + oncdataend, + oncdatastart, + onclosetag, + oncomment, + oncommentend, + onerror, + onopentag, + onprocessinginstruction, + onreset, + ontext + */ + + var formTags = { + input: true, + option: true, + optgroup: true, + select: true, + button: true, + datalist: true, + textarea: true + }; + + var openImpliesClose = { + tr : { tr:true, th:true, td:true }, + th : { th:true }, + td : { thead:true, th:true, td:true }, + body : { head:true, link:true, script:true }, + li : { li:true }, + p : { p:true }, + h1 : { p:true }, + h2 : { p:true }, + h3 : { p:true }, + h4 : { p:true }, + h5 : { p:true }, + h6 : { p:true }, + select : formTags, + input : formTags, + output : formTags, + button : formTags, + datalist: formTags, + textarea: formTags, + option : { option:true }, + optgroup: { optgroup:true } + }; + + var voidElements = { + __proto__: null, + area: true, + base: true, + basefont: true, + br: true, + col: true, + command: true, + embed: true, + frame: true, + hr: true, + img: true, + input: true, + isindex: true, + keygen: true, + link: true, + meta: true, + param: true, + source: true, + track: true, + wbr: true, + + //common self closing svg elements + path: true, + circle: true, + ellipse: true, + line: true, + rect: true, + use: true, + stop: true, + polyline: true, + polygon: true + }; + + var re_nameEnd = /\s|\//; + + function Parser(cbs, options){ + this._options = options || {}; + this._cbs = cbs || {}; + + this._tagname = ""; + this._attribname = ""; + this._attribvalue = ""; + this._attribs = null; + this._stack = []; + + this.startIndex = 0; + this.endIndex = null; + + this._lowerCaseTagNames = "lowerCaseTags" in this._options ? + !!this._options.lowerCaseTags : + !this._options.xmlMode; + this._lowerCaseAttributeNames = "lowerCaseAttributeNames" in this._options ? + !!this._options.lowerCaseAttributeNames : + !this._options.xmlMode; + if(!!this._options.Tokenizer) { + Tokenizer = this._options.Tokenizer; + } + this._tokenizer = new Tokenizer(this._options, this); + + if(this._cbs.onparserinit) this._cbs.onparserinit(this); + } + + __webpack_require__(2).inherits(Parser, __webpack_require__(1).EventEmitter); + + Parser.prototype._updatePosition = function(initialOffset){ + if(this.endIndex === null){ + if(this._tokenizer._sectionStart <= initialOffset){ + this.startIndex = 0; + } else { + this.startIndex = this._tokenizer._sectionStart - initialOffset; + } + } + else this.startIndex = this.endIndex + 1; + this.endIndex = this._tokenizer.getAbsoluteIndex(); + }; + + //Tokenizer event handlers + Parser.prototype.ontext = function(data){ + this._updatePosition(1); + this.endIndex--; + + if(this._cbs.ontext) this._cbs.ontext(data); + }; + + Parser.prototype.onopentagname = function(name){ + if(this._lowerCaseTagNames){ + name = name.toLowerCase(); + } + + this._tagname = name; + + if(!this._options.xmlMode && name in openImpliesClose) { + for( + var el; + (el = this._stack[this._stack.length - 1]) in openImpliesClose[name]; + this.onclosetag(el) + ); + } + + if(this._options.xmlMode || !(name in voidElements)){ + this._stack.push(name); + } + + if(this._cbs.onopentagname) this._cbs.onopentagname(name); + if(this._cbs.onopentag) this._attribs = {}; + }; + + Parser.prototype.onopentagend = function(){ + this._updatePosition(1); + + if(this._attribs){ + if(this._cbs.onopentag) this._cbs.onopentag(this._tagname, this._attribs); + this._attribs = null; + } + + if(!this._options.xmlMode && this._cbs.onclosetag && this._tagname in voidElements){ + this._cbs.onclosetag(this._tagname); + } + + this._tagname = ""; + }; + + Parser.prototype.onclosetag = function(name){ + this._updatePosition(1); + + if(this._lowerCaseTagNames){ + name = name.toLowerCase(); + } + + if(this._stack.length && (!(name in voidElements) || this._options.xmlMode)){ + var pos = this._stack.lastIndexOf(name); + if(pos !== -1){ + if(this._cbs.onclosetag){ + pos = this._stack.length - pos; + while(pos--) this._cbs.onclosetag(this._stack.pop()); + } + else this._stack.length = pos; + } else if(name === "p" && !this._options.xmlMode){ + this.onopentagname(name); + this._closeCurrentTag(); + } + } else if(!this._options.xmlMode && (name === "br" || name === "p")){ + this.onopentagname(name); + this._closeCurrentTag(); + } + }; + + Parser.prototype.onselfclosingtag = function(){ + if(this._options.xmlMode || this._options.recognizeSelfClosing){ + this._closeCurrentTag(); + } else { + this.onopentagend(); + } + }; + + Parser.prototype._closeCurrentTag = function(){ + var name = this._tagname; + + this.onopentagend(); + + //self-closing tags will be on the top of the stack + //(cheaper check than in onclosetag) + if(this._stack[this._stack.length - 1] === name){ + if(this._cbs.onclosetag){ + this._cbs.onclosetag(name); + } + this._stack.pop(); + } + }; + + Parser.prototype.onattribname = function(name){ + if(this._lowerCaseAttributeNames){ + name = name.toLowerCase(); + } + this._attribname = name; + }; + + Parser.prototype.onattribdata = function(value){ + this._attribvalue += value; + }; + + Parser.prototype.onattribend = function(){ + if(this._cbs.onattribute) this._cbs.onattribute(this._attribname, this._attribvalue); + if( + this._attribs && + !Object.prototype.hasOwnProperty.call(this._attribs, this._attribname) + ){ + this._attribs[this._attribname] = this._attribvalue; + } + this._attribname = ""; + this._attribvalue = ""; + }; + + Parser.prototype._getInstructionName = function(value){ + var idx = value.search(re_nameEnd), + name = idx < 0 ? value : value.substr(0, idx); + + if(this._lowerCaseTagNames){ + name = name.toLowerCase(); + } + + return name; + }; + + Parser.prototype.ondeclaration = function(value){ + if(this._cbs.onprocessinginstruction){ + var name = this._getInstructionName(value); + this._cbs.onprocessinginstruction("!" + name, "!" + value); + } + }; + + Parser.prototype.onprocessinginstruction = function(value){ + if(this._cbs.onprocessinginstruction){ + var name = this._getInstructionName(value); + this._cbs.onprocessinginstruction("?" + name, "?" + value); + } + }; + + Parser.prototype.oncomment = function(value){ + this._updatePosition(4); + + if(this._cbs.oncomment) this._cbs.oncomment(value); + if(this._cbs.oncommentend) this._cbs.oncommentend(); + }; + + Parser.prototype.oncdata = function(value){ + this._updatePosition(1); + + if(this._options.xmlMode || this._options.recognizeCDATA){ + if(this._cbs.oncdatastart) this._cbs.oncdatastart(); + if(this._cbs.ontext) this._cbs.ontext(value); + if(this._cbs.oncdataend) this._cbs.oncdataend(); + } else { + this.oncomment("[CDATA[" + value + "]]"); + } + }; + + Parser.prototype.onerror = function(err){ + if(this._cbs.onerror) this._cbs.onerror(err); + }; + + Parser.prototype.onend = function(){ + if(this._cbs.onclosetag){ + for( + var i = this._stack.length; + i > 0; + this._cbs.onclosetag(this._stack[--i]) + ); + } + if(this._cbs.onend) this._cbs.onend(); + }; + + + //Resets the parser to a blank state, ready to parse a new HTML document + Parser.prototype.reset = function(){ + if(this._cbs.onreset) this._cbs.onreset(); + this._tokenizer.reset(); + + this._tagname = ""; + this._attribname = ""; + this._attribs = null; + this._stack = []; + + if(this._cbs.onparserinit) this._cbs.onparserinit(this); + }; + + //Parses a complete HTML document and pushes it to the handler + Parser.prototype.parseComplete = function(data){ + this.reset(); + this.end(data); + }; + + Parser.prototype.write = function(chunk){ + this._tokenizer.write(chunk); + }; + + Parser.prototype.end = function(chunk){ + this._tokenizer.end(chunk); + }; + + Parser.prototype.pause = function(){ + this._tokenizer.pause(); + }; + + Parser.prototype.resume = function(){ + this._tokenizer.resume(); + }; + + //alias for backwards compat + Parser.prototype.parseChunk = Parser.prototype.write; + Parser.prototype.done = Parser.prototype.end; + + module.exports = Parser; + + +/***/ }, +/* 29 */ +/***/ function(module, exports, __webpack_require__) { + + module.exports = Tokenizer; + + var decodeCodePoint = __webpack_require__(30), + entityMap = __webpack_require__(32), + legacyMap = __webpack_require__(33), + xmlMap = __webpack_require__(34), + + i = 0, + + TEXT = i++, + BEFORE_TAG_NAME = i++, //after < + IN_TAG_NAME = i++, + IN_SELF_CLOSING_TAG = i++, + BEFORE_CLOSING_TAG_NAME = i++, + IN_CLOSING_TAG_NAME = i++, + AFTER_CLOSING_TAG_NAME = i++, + + //attributes + BEFORE_ATTRIBUTE_NAME = i++, + IN_ATTRIBUTE_NAME = i++, + AFTER_ATTRIBUTE_NAME = i++, + BEFORE_ATTRIBUTE_VALUE = i++, + IN_ATTRIBUTE_VALUE_DQ = i++, // " + IN_ATTRIBUTE_VALUE_SQ = i++, // ' + IN_ATTRIBUTE_VALUE_NQ = i++, + + //declarations + BEFORE_DECLARATION = i++, // ! + IN_DECLARATION = i++, + + //processing instructions + IN_PROCESSING_INSTRUCTION = i++, // ? + + //comments + BEFORE_COMMENT = i++, + IN_COMMENT = i++, + AFTER_COMMENT_1 = i++, + AFTER_COMMENT_2 = i++, + + //cdata + BEFORE_CDATA_1 = i++, // [ + BEFORE_CDATA_2 = i++, // C + BEFORE_CDATA_3 = i++, // D + BEFORE_CDATA_4 = i++, // A + BEFORE_CDATA_5 = i++, // T + BEFORE_CDATA_6 = i++, // A + IN_CDATA = i++, // [ + AFTER_CDATA_1 = i++, // ] + AFTER_CDATA_2 = i++, // ] + + //special tags + BEFORE_SPECIAL = i++, //S + BEFORE_SPECIAL_END = i++, //S + + BEFORE_SCRIPT_1 = i++, //C + BEFORE_SCRIPT_2 = i++, //R + BEFORE_SCRIPT_3 = i++, //I + BEFORE_SCRIPT_4 = i++, //P + BEFORE_SCRIPT_5 = i++, //T + AFTER_SCRIPT_1 = i++, //C + AFTER_SCRIPT_2 = i++, //R + AFTER_SCRIPT_3 = i++, //I + AFTER_SCRIPT_4 = i++, //P + AFTER_SCRIPT_5 = i++, //T + + BEFORE_STYLE_1 = i++, //T + BEFORE_STYLE_2 = i++, //Y + BEFORE_STYLE_3 = i++, //L + BEFORE_STYLE_4 = i++, //E + AFTER_STYLE_1 = i++, //T + AFTER_STYLE_2 = i++, //Y + AFTER_STYLE_3 = i++, //L + AFTER_STYLE_4 = i++, //E + + BEFORE_ENTITY = i++, //& + BEFORE_NUMERIC_ENTITY = i++, //# + IN_NAMED_ENTITY = i++, + IN_NUMERIC_ENTITY = i++, + IN_HEX_ENTITY = i++, //X + + j = 0, + + SPECIAL_NONE = j++, + SPECIAL_SCRIPT = j++, + SPECIAL_STYLE = j++; + + function whitespace(c){ + return c === " " || c === "\n" || c === "\t" || c === "\f" || c === "\r"; + } + + function characterState(char, SUCCESS){ + return function(c){ + if(c === char) this._state = SUCCESS; + }; + } + + function ifElseState(upper, SUCCESS, FAILURE){ + var lower = upper.toLowerCase(); + + if(upper === lower){ + return function(c){ + if(c === lower){ + this._state = SUCCESS; + } else { + this._state = FAILURE; + this._index--; + } + }; + } else { + return function(c){ + if(c === lower || c === upper){ + this._state = SUCCESS; + } else { + this._state = FAILURE; + this._index--; + } + }; + } + } + + function consumeSpecialNameChar(upper, NEXT_STATE){ + var lower = upper.toLowerCase(); + + return function(c){ + if(c === lower || c === upper){ + this._state = NEXT_STATE; + } else { + this._state = IN_TAG_NAME; + this._index--; //consume the token again + } + }; + } + + function Tokenizer(options, cbs){ + this._state = TEXT; + this._buffer = ""; + this._sectionStart = 0; + this._index = 0; + this._bufferOffset = 0; //chars removed from _buffer + this._baseState = TEXT; + this._special = SPECIAL_NONE; + this._cbs = cbs; + this._running = true; + this._ended = false; + this._xmlMode = !!(options && options.xmlMode); + this._decodeEntities = !!(options && options.decodeEntities); + } + + Tokenizer.prototype._stateText = function(c){ + if(c === "<"){ + if(this._index > this._sectionStart){ + this._cbs.ontext(this._getSection()); + } + this._state = BEFORE_TAG_NAME; + this._sectionStart = this._index; + } else if(this._decodeEntities && this._special === SPECIAL_NONE && c === "&"){ + if(this._index > this._sectionStart){ + this._cbs.ontext(this._getSection()); + } + this._baseState = TEXT; + this._state = BEFORE_ENTITY; + this._sectionStart = this._index; + } + }; + + Tokenizer.prototype._stateBeforeTagName = function(c){ + if(c === "/"){ + this._state = BEFORE_CLOSING_TAG_NAME; + } else if(c === ">" || this._special !== SPECIAL_NONE || whitespace(c)) { + this._state = TEXT; + } else if(c === "!"){ + this._state = BEFORE_DECLARATION; + this._sectionStart = this._index + 1; + } else if(c === "?"){ + this._state = IN_PROCESSING_INSTRUCTION; + this._sectionStart = this._index + 1; + } else if(c === "<"){ + this._cbs.ontext(this._getSection()); + this._sectionStart = this._index; + } else { + this._state = (!this._xmlMode && (c === "s" || c === "S")) ? + BEFORE_SPECIAL : IN_TAG_NAME; + this._sectionStart = this._index; + } + }; + + Tokenizer.prototype._stateInTagName = function(c){ + if(c === "/" || c === ">" || whitespace(c)){ + this._emitToken("onopentagname"); + this._state = BEFORE_ATTRIBUTE_NAME; + this._index--; + } + }; + + Tokenizer.prototype._stateBeforeCloseingTagName = function(c){ + if(whitespace(c)); + else if(c === ">"){ + this._state = TEXT; + } else if(this._special !== SPECIAL_NONE){ + if(c === "s" || c === "S"){ + this._state = BEFORE_SPECIAL_END; + } else { + this._state = TEXT; + this._index--; + } + } else { + this._state = IN_CLOSING_TAG_NAME; + this._sectionStart = this._index; + } + }; + + Tokenizer.prototype._stateInCloseingTagName = function(c){ + if(c === ">" || whitespace(c)){ + this._emitToken("onclosetag"); + this._state = AFTER_CLOSING_TAG_NAME; + this._index--; + } + }; + + Tokenizer.prototype._stateAfterCloseingTagName = function(c){ + //skip everything until ">" + if(c === ">"){ + this._state = TEXT; + this._sectionStart = this._index + 1; + } + }; + + Tokenizer.prototype._stateBeforeAttributeName = function(c){ + if(c === ">"){ + this._cbs.onopentagend(); + this._state = TEXT; + this._sectionStart = this._index + 1; + } else if(c === "/"){ + this._state = IN_SELF_CLOSING_TAG; + } else if(!whitespace(c)){ + this._state = IN_ATTRIBUTE_NAME; + this._sectionStart = this._index; + } + }; + + Tokenizer.prototype._stateInSelfClosingTag = function(c){ + if(c === ">"){ + this._cbs.onselfclosingtag(); + this._state = TEXT; + this._sectionStart = this._index + 1; + } else if(!whitespace(c)){ + this._state = BEFORE_ATTRIBUTE_NAME; + this._index--; + } + }; + + Tokenizer.prototype._stateInAttributeName = function(c){ + if(c === "=" || c === "/" || c === ">" || whitespace(c)){ + this._cbs.onattribname(this._getSection()); + this._sectionStart = -1; + this._state = AFTER_ATTRIBUTE_NAME; + this._index--; + } + }; + + Tokenizer.prototype._stateAfterAttributeName = function(c){ + if(c === "="){ + this._state = BEFORE_ATTRIBUTE_VALUE; + } else if(c === "/" || c === ">"){ + this._cbs.onattribend(); + this._state = BEFORE_ATTRIBUTE_NAME; + this._index--; + } else if(!whitespace(c)){ + this._cbs.onattribend(); + this._state = IN_ATTRIBUTE_NAME; + this._sectionStart = this._index; + } + }; + + Tokenizer.prototype._stateBeforeAttributeValue = function(c){ + if(c === "\""){ + this._state = IN_ATTRIBUTE_VALUE_DQ; + this._sectionStart = this._index + 1; + } else if(c === "'"){ + this._state = IN_ATTRIBUTE_VALUE_SQ; + this._sectionStart = this._index + 1; + } else if(!whitespace(c)){ + this._state = IN_ATTRIBUTE_VALUE_NQ; + this._sectionStart = this._index; + this._index--; //reconsume token + } + }; + + Tokenizer.prototype._stateInAttributeValueDoubleQuotes = function(c){ + if(c === "\""){ + this._emitToken("onattribdata"); + this._cbs.onattribend(); + this._state = BEFORE_ATTRIBUTE_NAME; + } else if(this._decodeEntities && c === "&"){ + this._emitToken("onattribdata"); + this._baseState = this._state; + this._state = BEFORE_ENTITY; + this._sectionStart = this._index; + } + }; + + Tokenizer.prototype._stateInAttributeValueSingleQuotes = function(c){ + if(c === "'"){ + this._emitToken("onattribdata"); + this._cbs.onattribend(); + this._state = BEFORE_ATTRIBUTE_NAME; + } else if(this._decodeEntities && c === "&"){ + this._emitToken("onattribdata"); + this._baseState = this._state; + this._state = BEFORE_ENTITY; + this._sectionStart = this._index; + } + }; + + Tokenizer.prototype._stateInAttributeValueNoQuotes = function(c){ + if(whitespace(c) || c === ">"){ + this._emitToken("onattribdata"); + this._cbs.onattribend(); + this._state = BEFORE_ATTRIBUTE_NAME; + this._index--; + } else if(this._decodeEntities && c === "&"){ + this._emitToken("onattribdata"); + this._baseState = this._state; + this._state = BEFORE_ENTITY; + this._sectionStart = this._index; + } + }; + + Tokenizer.prototype._stateBeforeDeclaration = function(c){ + this._state = c === "[" ? BEFORE_CDATA_1 : + c === "-" ? BEFORE_COMMENT : + IN_DECLARATION; + }; + + Tokenizer.prototype._stateInDeclaration = function(c){ + if(c === ">"){ + this._cbs.ondeclaration(this._getSection()); + this._state = TEXT; + this._sectionStart = this._index + 1; + } + }; + + Tokenizer.prototype._stateInProcessingInstruction = function(c){ + if(c === ">"){ + this._cbs.onprocessinginstruction(this._getSection()); + this._state = TEXT; + this._sectionStart = this._index + 1; + } + }; + + Tokenizer.prototype._stateBeforeComment = function(c){ + if(c === "-"){ + this._state = IN_COMMENT; + this._sectionStart = this._index + 1; + } else { + this._state = IN_DECLARATION; + } + }; + + Tokenizer.prototype._stateInComment = function(c){ + if(c === "-") this._state = AFTER_COMMENT_1; + }; + + Tokenizer.prototype._stateAfterComment1 = function(c){ + if(c === "-"){ + this._state = AFTER_COMMENT_2; + } else { + this._state = IN_COMMENT; + } + }; + + Tokenizer.prototype._stateAfterComment2 = function(c){ + if(c === ">"){ + //remove 2 trailing chars + this._cbs.oncomment(this._buffer.substring(this._sectionStart, this._index - 2)); + this._state = TEXT; + this._sectionStart = this._index + 1; + } else if(c !== "-"){ + this._state = IN_COMMENT; + } + // else: stay in AFTER_COMMENT_2 (`--->`) + }; + + Tokenizer.prototype._stateBeforeCdata1 = ifElseState("C", BEFORE_CDATA_2, IN_DECLARATION); + Tokenizer.prototype._stateBeforeCdata2 = ifElseState("D", BEFORE_CDATA_3, IN_DECLARATION); + Tokenizer.prototype._stateBeforeCdata3 = ifElseState("A", BEFORE_CDATA_4, IN_DECLARATION); + Tokenizer.prototype._stateBeforeCdata4 = ifElseState("T", BEFORE_CDATA_5, IN_DECLARATION); + Tokenizer.prototype._stateBeforeCdata5 = ifElseState("A", BEFORE_CDATA_6, IN_DECLARATION); + + Tokenizer.prototype._stateBeforeCdata6 = function(c){ + if(c === "["){ + this._state = IN_CDATA; + this._sectionStart = this._index + 1; + } else { + this._state = IN_DECLARATION; + this._index--; + } + }; + + Tokenizer.prototype._stateInCdata = function(c){ + if(c === "]") this._state = AFTER_CDATA_1; + }; + + Tokenizer.prototype._stateAfterCdata1 = characterState("]", AFTER_CDATA_2); + + Tokenizer.prototype._stateAfterCdata2 = function(c){ + if(c === ">"){ + //remove 2 trailing chars + this._cbs.oncdata(this._buffer.substring(this._sectionStart, this._index - 2)); + this._state = TEXT; + this._sectionStart = this._index + 1; + } else if(c !== "]") { + this._state = IN_CDATA; + } + //else: stay in AFTER_CDATA_2 (`]]]>`) + }; + + Tokenizer.prototype._stateBeforeSpecial = function(c){ + if(c === "c" || c === "C"){ + this._state = BEFORE_SCRIPT_1; + } else if(c === "t" || c === "T"){ + this._state = BEFORE_STYLE_1; + } else { + this._state = IN_TAG_NAME; + this._index--; //consume the token again + } + }; + + Tokenizer.prototype._stateBeforeSpecialEnd = function(c){ + if(this._special === SPECIAL_SCRIPT && (c === "c" || c === "C")){ + this._state = AFTER_SCRIPT_1; + } else if(this._special === SPECIAL_STYLE && (c === "t" || c === "T")){ + this._state = AFTER_STYLE_1; + } + else this._state = TEXT; + }; + + Tokenizer.prototype._stateBeforeScript1 = consumeSpecialNameChar("R", BEFORE_SCRIPT_2); + Tokenizer.prototype._stateBeforeScript2 = consumeSpecialNameChar("I", BEFORE_SCRIPT_3); + Tokenizer.prototype._stateBeforeScript3 = consumeSpecialNameChar("P", BEFORE_SCRIPT_4); + Tokenizer.prototype._stateBeforeScript4 = consumeSpecialNameChar("T", BEFORE_SCRIPT_5); + + Tokenizer.prototype._stateBeforeScript5 = function(c){ + if(c === "/" || c === ">" || whitespace(c)){ + this._special = SPECIAL_SCRIPT; + } + this._state = IN_TAG_NAME; + this._index--; //consume the token again + }; + + Tokenizer.prototype._stateAfterScript1 = ifElseState("R", AFTER_SCRIPT_2, TEXT); + Tokenizer.prototype._stateAfterScript2 = ifElseState("I", AFTER_SCRIPT_3, TEXT); + Tokenizer.prototype._stateAfterScript3 = ifElseState("P", AFTER_SCRIPT_4, TEXT); + Tokenizer.prototype._stateAfterScript4 = ifElseState("T", AFTER_SCRIPT_5, TEXT); + + Tokenizer.prototype._stateAfterScript5 = function(c){ + if(c === ">" || whitespace(c)){ + this._special = SPECIAL_NONE; + this._state = IN_CLOSING_TAG_NAME; + this._sectionStart = this._index - 6; + this._index--; //reconsume the token + } + else this._state = TEXT; + }; + + Tokenizer.prototype._stateBeforeStyle1 = consumeSpecialNameChar("Y", BEFORE_STYLE_2); + Tokenizer.prototype._stateBeforeStyle2 = consumeSpecialNameChar("L", BEFORE_STYLE_3); + Tokenizer.prototype._stateBeforeStyle3 = consumeSpecialNameChar("E", BEFORE_STYLE_4); + + Tokenizer.prototype._stateBeforeStyle4 = function(c){ + if(c === "/" || c === ">" || whitespace(c)){ + this._special = SPECIAL_STYLE; + } + this._state = IN_TAG_NAME; + this._index--; //consume the token again + }; + + Tokenizer.prototype._stateAfterStyle1 = ifElseState("Y", AFTER_STYLE_2, TEXT); + Tokenizer.prototype._stateAfterStyle2 = ifElseState("L", AFTER_STYLE_3, TEXT); + Tokenizer.prototype._stateAfterStyle3 = ifElseState("E", AFTER_STYLE_4, TEXT); + + Tokenizer.prototype._stateAfterStyle4 = function(c){ + if(c === ">" || whitespace(c)){ + this._special = SPECIAL_NONE; + this._state = IN_CLOSING_TAG_NAME; + this._sectionStart = this._index - 5; + this._index--; //reconsume the token + } + else this._state = TEXT; + }; + + Tokenizer.prototype._stateBeforeEntity = ifElseState("#", BEFORE_NUMERIC_ENTITY, IN_NAMED_ENTITY); + Tokenizer.prototype._stateBeforeNumericEntity = ifElseState("X", IN_HEX_ENTITY, IN_NUMERIC_ENTITY); + + //for entities terminated with a semicolon + Tokenizer.prototype._parseNamedEntityStrict = function(){ + //offset = 1 + if(this._sectionStart + 1 < this._index){ + var entity = this._buffer.substring(this._sectionStart + 1, this._index), + map = this._xmlMode ? xmlMap : entityMap; + + if(map.hasOwnProperty(entity)){ + this._emitPartial(map[entity]); + this._sectionStart = this._index + 1; + } + } + }; + + + //parses legacy entities (without trailing semicolon) + Tokenizer.prototype._parseLegacyEntity = function(){ + var start = this._sectionStart + 1, + limit = this._index - start; + + if(limit > 6) limit = 6; //the max length of legacy entities is 6 + + while(limit >= 2){ //the min length of legacy entities is 2 + var entity = this._buffer.substr(start, limit); + + if(legacyMap.hasOwnProperty(entity)){ + this._emitPartial(legacyMap[entity]); + this._sectionStart += limit + 1; + return; + } else { + limit--; + } + } + }; + + Tokenizer.prototype._stateInNamedEntity = function(c){ + if(c === ";"){ + this._parseNamedEntityStrict(); + if(this._sectionStart + 1 < this._index && !this._xmlMode){ + this._parseLegacyEntity(); + } + this._state = this._baseState; + } else if((c < "a" || c > "z") && (c < "A" || c > "Z") && (c < "0" || c > "9")){ + if(this._xmlMode); + else if(this._sectionStart + 1 === this._index); + else if(this._baseState !== TEXT){ + if(c !== "="){ + this._parseNamedEntityStrict(); + } + } else { + this._parseLegacyEntity(); + } + + this._state = this._baseState; + this._index--; + } + }; + + Tokenizer.prototype._decodeNumericEntity = function(offset, base){ + var sectionStart = this._sectionStart + offset; + + if(sectionStart !== this._index){ + //parse entity + var entity = this._buffer.substring(sectionStart, this._index); + var parsed = parseInt(entity, base); + + this._emitPartial(decodeCodePoint(parsed)); + this._sectionStart = this._index; + } else { + this._sectionStart--; + } + + this._state = this._baseState; + }; + + Tokenizer.prototype._stateInNumericEntity = function(c){ + if(c === ";"){ + this._decodeNumericEntity(2, 10); + this._sectionStart++; + } else if(c < "0" || c > "9"){ + if(!this._xmlMode){ + this._decodeNumericEntity(2, 10); + } else { + this._state = this._baseState; + } + this._index--; + } + }; + + Tokenizer.prototype._stateInHexEntity = function(c){ + if(c === ";"){ + this._decodeNumericEntity(3, 16); + this._sectionStart++; + } else if((c < "a" || c > "f") && (c < "A" || c > "F") && (c < "0" || c > "9")){ + if(!this._xmlMode){ + this._decodeNumericEntity(3, 16); + } else { + this._state = this._baseState; + } + this._index--; + } + }; + + Tokenizer.prototype._cleanup = function (){ + if(this._sectionStart < 0){ + this._buffer = ""; + this._index = 0; + this._bufferOffset += this._index; + } else if(this._running){ + if(this._state === TEXT){ + if(this._sectionStart !== this._index){ + this._cbs.ontext(this._buffer.substr(this._sectionStart)); + } + this._buffer = ""; + this._index = 0; + this._bufferOffset += this._index; + } else if(this._sectionStart === this._index){ + //the section just started + this._buffer = ""; + this._index = 0; + this._bufferOffset += this._index; + } else { + //remove everything unnecessary + this._buffer = this._buffer.substr(this._sectionStart); + this._index -= this._sectionStart; + this._bufferOffset += this._sectionStart; + } + + this._sectionStart = 0; + } + }; + + //TODO make events conditional + Tokenizer.prototype.write = function(chunk){ + if(this._ended) this._cbs.onerror(Error(".write() after done!")); + + this._buffer += chunk; + this._parse(); + }; + + Tokenizer.prototype._parse = function(){ + while(this._index < this._buffer.length && this._running){ + var c = this._buffer.charAt(this._index); + if(this._state === TEXT) { + this._stateText(c); + } else if(this._state === BEFORE_TAG_NAME){ + this._stateBeforeTagName(c); + } else if(this._state === IN_TAG_NAME) { + this._stateInTagName(c); + } else if(this._state === BEFORE_CLOSING_TAG_NAME){ + this._stateBeforeCloseingTagName(c); + } else if(this._state === IN_CLOSING_TAG_NAME){ + this._stateInCloseingTagName(c); + } else if(this._state === AFTER_CLOSING_TAG_NAME){ + this._stateAfterCloseingTagName(c); + } else if(this._state === IN_SELF_CLOSING_TAG){ + this._stateInSelfClosingTag(c); + } + + /* + * attributes + */ + else if(this._state === BEFORE_ATTRIBUTE_NAME){ + this._stateBeforeAttributeName(c); + } else if(this._state === IN_ATTRIBUTE_NAME){ + this._stateInAttributeName(c); + } else if(this._state === AFTER_ATTRIBUTE_NAME){ + this._stateAfterAttributeName(c); + } else if(this._state === BEFORE_ATTRIBUTE_VALUE){ + this._stateBeforeAttributeValue(c); + } else if(this._state === IN_ATTRIBUTE_VALUE_DQ){ + this._stateInAttributeValueDoubleQuotes(c); + } else if(this._state === IN_ATTRIBUTE_VALUE_SQ){ + this._stateInAttributeValueSingleQuotes(c); + } else if(this._state === IN_ATTRIBUTE_VALUE_NQ){ + this._stateInAttributeValueNoQuotes(c); + } + + /* + * declarations + */ + else if(this._state === BEFORE_DECLARATION){ + this._stateBeforeDeclaration(c); + } else if(this._state === IN_DECLARATION){ + this._stateInDeclaration(c); + } + + /* + * processing instructions + */ + else if(this._state === IN_PROCESSING_INSTRUCTION){ + this._stateInProcessingInstruction(c); + } + + /* + * comments + */ + else if(this._state === BEFORE_COMMENT){ + this._stateBeforeComment(c); + } else if(this._state === IN_COMMENT){ + this._stateInComment(c); + } else if(this._state === AFTER_COMMENT_1){ + this._stateAfterComment1(c); + } else if(this._state === AFTER_COMMENT_2){ + this._stateAfterComment2(c); + } + + /* + * cdata + */ + else if(this._state === BEFORE_CDATA_1){ + this._stateBeforeCdata1(c); + } else if(this._state === BEFORE_CDATA_2){ + this._stateBeforeCdata2(c); + } else if(this._state === BEFORE_CDATA_3){ + this._stateBeforeCdata3(c); + } else if(this._state === BEFORE_CDATA_4){ + this._stateBeforeCdata4(c); + } else if(this._state === BEFORE_CDATA_5){ + this._stateBeforeCdata5(c); + } else if(this._state === BEFORE_CDATA_6){ + this._stateBeforeCdata6(c); + } else if(this._state === IN_CDATA){ + this._stateInCdata(c); + } else if(this._state === AFTER_CDATA_1){ + this._stateAfterCdata1(c); + } else if(this._state === AFTER_CDATA_2){ + this._stateAfterCdata2(c); + } + + /* + * special tags + */ + else if(this._state === BEFORE_SPECIAL){ + this._stateBeforeSpecial(c); + } else if(this._state === BEFORE_SPECIAL_END){ + this._stateBeforeSpecialEnd(c); + } + + /* + * script + */ + else if(this._state === BEFORE_SCRIPT_1){ + this._stateBeforeScript1(c); + } else if(this._state === BEFORE_SCRIPT_2){ + this._stateBeforeScript2(c); + } else if(this._state === BEFORE_SCRIPT_3){ + this._stateBeforeScript3(c); + } else if(this._state === BEFORE_SCRIPT_4){ + this._stateBeforeScript4(c); + } else if(this._state === BEFORE_SCRIPT_5){ + this._stateBeforeScript5(c); + } + + else if(this._state === AFTER_SCRIPT_1){ + this._stateAfterScript1(c); + } else if(this._state === AFTER_SCRIPT_2){ + this._stateAfterScript2(c); + } else if(this._state === AFTER_SCRIPT_3){ + this._stateAfterScript3(c); + } else if(this._state === AFTER_SCRIPT_4){ + this._stateAfterScript4(c); + } else if(this._state === AFTER_SCRIPT_5){ + this._stateAfterScript5(c); + } + + /* + * style + */ + else if(this._state === BEFORE_STYLE_1){ + this._stateBeforeStyle1(c); + } else if(this._state === BEFORE_STYLE_2){ + this._stateBeforeStyle2(c); + } else if(this._state === BEFORE_STYLE_3){ + this._stateBeforeStyle3(c); + } else if(this._state === BEFORE_STYLE_4){ + this._stateBeforeStyle4(c); + } + + else if(this._state === AFTER_STYLE_1){ + this._stateAfterStyle1(c); + } else if(this._state === AFTER_STYLE_2){ + this._stateAfterStyle2(c); + } else if(this._state === AFTER_STYLE_3){ + this._stateAfterStyle3(c); + } else if(this._state === AFTER_STYLE_4){ + this._stateAfterStyle4(c); + } + + /* + * entities + */ + else if(this._state === BEFORE_ENTITY){ + this._stateBeforeEntity(c); + } else if(this._state === BEFORE_NUMERIC_ENTITY){ + this._stateBeforeNumericEntity(c); + } else if(this._state === IN_NAMED_ENTITY){ + this._stateInNamedEntity(c); + } else if(this._state === IN_NUMERIC_ENTITY){ + this._stateInNumericEntity(c); + } else if(this._state === IN_HEX_ENTITY){ + this._stateInHexEntity(c); + } + + else { + this._cbs.onerror(Error("unknown _state"), this._state); + } + + this._index++; + } + + this._cleanup(); + }; + + Tokenizer.prototype.pause = function(){ + this._running = false; + }; + Tokenizer.prototype.resume = function(){ + this._running = true; + + if(this._index < this._buffer.length){ + this._parse(); + } + if(this._ended){ + this._finish(); + } + }; + + Tokenizer.prototype.end = function(chunk){ + if(this._ended) this._cbs.onerror(Error(".end() after done!")); + if(chunk) this.write(chunk); + + this._ended = true; + + if(this._running) this._finish(); + }; + + Tokenizer.prototype._finish = function(){ + //if there is remaining data, emit it in a reasonable way + if(this._sectionStart < this._index){ + this._handleTrailingData(); + } + + this._cbs.onend(); + }; + + Tokenizer.prototype._handleTrailingData = function(){ + var data = this._buffer.substr(this._sectionStart); + + if(this._state === IN_CDATA || this._state === AFTER_CDATA_1 || this._state === AFTER_CDATA_2){ + this._cbs.oncdata(data); + } else if(this._state === IN_COMMENT || this._state === AFTER_COMMENT_1 || this._state === AFTER_COMMENT_2){ + this._cbs.oncomment(data); + } else if(this._state === IN_NAMED_ENTITY && !this._xmlMode){ + this._parseLegacyEntity(); + if(this._sectionStart < this._index){ + this._state = this._baseState; + this._handleTrailingData(); + } + } else if(this._state === IN_NUMERIC_ENTITY && !this._xmlMode){ + this._decodeNumericEntity(2, 10); + if(this._sectionStart < this._index){ + this._state = this._baseState; + this._handleTrailingData(); + } + } else if(this._state === IN_HEX_ENTITY && !this._xmlMode){ + this._decodeNumericEntity(3, 16); + if(this._sectionStart < this._index){ + this._state = this._baseState; + this._handleTrailingData(); + } + } else if( + this._state !== IN_TAG_NAME && + this._state !== BEFORE_ATTRIBUTE_NAME && + this._state !== BEFORE_ATTRIBUTE_VALUE && + this._state !== AFTER_ATTRIBUTE_NAME && + this._state !== IN_ATTRIBUTE_NAME && + this._state !== IN_ATTRIBUTE_VALUE_SQ && + this._state !== IN_ATTRIBUTE_VALUE_DQ && + this._state !== IN_ATTRIBUTE_VALUE_NQ && + this._state !== IN_CLOSING_TAG_NAME + ){ + this._cbs.ontext(data); + } + //else, ignore remaining data + //TODO add a way to remove current tag + }; + + Tokenizer.prototype.reset = function(){ + Tokenizer.call(this, {xmlMode: this._xmlMode, decodeEntities: this._decodeEntities}, this._cbs); + }; + + Tokenizer.prototype.getAbsoluteIndex = function(){ + return this._bufferOffset + this._index; + }; + + Tokenizer.prototype._getSection = function(){ + return this._buffer.substring(this._sectionStart, this._index); + }; + + Tokenizer.prototype._emitToken = function(name){ + this._cbs[name](this._getSection()); + this._sectionStart = -1; + }; + + Tokenizer.prototype._emitPartial = function(value){ + if(this._baseState !== TEXT){ + this._cbs.onattribdata(value); //TODO implement the new event + } else { + this._cbs.ontext(value); + } + }; + + +/***/ }, +/* 30 */ +/***/ function(module, exports, __webpack_require__) { + + var decodeMap = __webpack_require__(31); + + module.exports = decodeCodePoint; + + // modified version of https://github.com/mathiasbynens/he/blob/master/src/he.js#L94-L119 + function decodeCodePoint(codePoint){ + + if((codePoint >= 0xD800 && codePoint <= 0xDFFF) || codePoint > 0x10FFFF){ + return "\uFFFD"; + } + + if(codePoint in decodeMap){ + codePoint = decodeMap[codePoint]; + } + + var output = ""; + + if(codePoint > 0xFFFF){ + codePoint -= 0x10000; + output += String.fromCharCode(codePoint >>> 10 & 0x3FF | 0xD800); + codePoint = 0xDC00 | codePoint & 0x3FF; + } + + output += String.fromCharCode(codePoint); + return output; + } + + +/***/ }, +/* 31 */ +/***/ function(module, exports) { + + module.exports = { + "0": 65533, + "128": 8364, + "130": 8218, + "131": 402, + "132": 8222, + "133": 8230, + "134": 8224, + "135": 8225, + "136": 710, + "137": 8240, + "138": 352, + "139": 8249, + "140": 338, + "142": 381, + "145": 8216, + "146": 8217, + "147": 8220, + "148": 8221, + "149": 8226, + "150": 8211, + "151": 8212, + "152": 732, + "153": 8482, + "154": 353, + "155": 8250, + "156": 339, + "158": 382, + "159": 376 + }; + +/***/ }, +/* 32 */ +/***/ function(module, exports) { + + module.exports = { + "Aacute": "Á", + "aacute": "á", + "Abreve": "Ă", + "abreve": "ă", + "ac": "∾", + "acd": "∿", + "acE": "∾̳", + "Acirc": "Â", + "acirc": "â", + "acute": "´", + "Acy": "А", + "acy": "а", + "AElig": "Æ", + "aelig": "æ", + "af": "⁡", + "Afr": "𝔄", + "afr": "𝔞", + "Agrave": "À", + "agrave": "à", + "alefsym": "ℵ", + "aleph": "ℵ", + "Alpha": "Α", + "alpha": "α", + "Amacr": "Ā", + "amacr": "ā", + "amalg": "⨿", + "amp": "&", + "AMP": "&", + "andand": "⩕", + "And": "⩓", + "and": "∧", + "andd": "⩜", + "andslope": "⩘", + "andv": "⩚", + "ang": "∠", + "ange": "⦤", + "angle": "∠", + "angmsdaa": "⦨", + "angmsdab": "⦩", + "angmsdac": "⦪", + "angmsdad": "⦫", + "angmsdae": "⦬", + "angmsdaf": "⦭", + "angmsdag": "⦮", + "angmsdah": "⦯", + "angmsd": "∡", + "angrt": "∟", + "angrtvb": "⊾", + "angrtvbd": "⦝", + "angsph": "∢", + "angst": "Å", + "angzarr": "⍼", + "Aogon": "Ą", + "aogon": "ą", + "Aopf": "𝔸", + "aopf": "𝕒", + "apacir": "⩯", + "ap": "≈", + "apE": "⩰", + "ape": "≊", + "apid": "≋", + "apos": "'", + "ApplyFunction": "⁡", + "approx": "≈", + "approxeq": "≊", + "Aring": "Å", + "aring": "å", + "Ascr": "𝒜", + "ascr": "𝒶", + "Assign": "≔", + "ast": "*", + "asymp": "≈", + "asympeq": "≍", + "Atilde": "Ã", + "atilde": "ã", + "Auml": "Ä", + "auml": "ä", + "awconint": "∳", + "awint": "⨑", + "backcong": "≌", + "backepsilon": "϶", + "backprime": "‵", + "backsim": "∽", + "backsimeq": "⋍", + "Backslash": "∖", + "Barv": "⫧", + "barvee": "⊽", + "barwed": "⌅", + "Barwed": "⌆", + "barwedge": "⌅", + "bbrk": "⎵", + "bbrktbrk": "⎶", + "bcong": "≌", + "Bcy": "Б", + "bcy": "б", + "bdquo": "„", + "becaus": "∵", + "because": "∵", + "Because": "∵", + "bemptyv": "⦰", + "bepsi": "϶", + "bernou": "ℬ", + "Bernoullis": "ℬ", + "Beta": "Β", + "beta": "β", + "beth": "ℶ", + "between": "≬", + "Bfr": "𝔅", + "bfr": "𝔟", + "bigcap": "⋂", + "bigcirc": "◯", + "bigcup": "⋃", + "bigodot": "⨀", + "bigoplus": "⨁", + "bigotimes": "⨂", + "bigsqcup": "⨆", + "bigstar": "★", + "bigtriangledown": "▽", + "bigtriangleup": "△", + "biguplus": "⨄", + "bigvee": "⋁", + "bigwedge": "⋀", + "bkarow": "⤍", + "blacklozenge": "⧫", + "blacksquare": "▪", + "blacktriangle": "▴", + "blacktriangledown": "▾", + "blacktriangleleft": "◂", + "blacktriangleright": "▸", + "blank": "␣", + "blk12": "▒", + "blk14": "░", + "blk34": "▓", + "block": "█", + "bne": "=⃥", + "bnequiv": "≡⃥", + "bNot": "⫭", + "bnot": "⌐", + "Bopf": "𝔹", + "bopf": "𝕓", + "bot": "⊥", + "bottom": "⊥", + "bowtie": "⋈", + "boxbox": "⧉", + "boxdl": "┐", + "boxdL": "╕", + "boxDl": "╖", + "boxDL": "╗", + "boxdr": "┌", + "boxdR": "╒", + "boxDr": "╓", + "boxDR": "╔", + "boxh": "─", + "boxH": "═", + "boxhd": "┬", + "boxHd": "╤", + "boxhD": "╥", + "boxHD": "╦", + "boxhu": "┴", + "boxHu": "╧", + "boxhU": "╨", + "boxHU": "╩", + "boxminus": "⊟", + "boxplus": "⊞", + "boxtimes": "⊠", + "boxul": "┘", + "boxuL": "╛", + "boxUl": "╜", + "boxUL": "╝", + "boxur": "└", + "boxuR": "╘", + "boxUr": "╙", + "boxUR": "╚", + "boxv": "│", + "boxV": "║", + "boxvh": "┼", + "boxvH": "╪", + "boxVh": "╫", + "boxVH": "╬", + "boxvl": "┤", + "boxvL": "╡", + "boxVl": "╢", + "boxVL": "╣", + "boxvr": "├", + "boxvR": "╞", + "boxVr": "╟", + "boxVR": "╠", + "bprime": "‵", + "breve": "˘", + "Breve": "˘", + "brvbar": "¦", + "bscr": "𝒷", + "Bscr": "ℬ", + "bsemi": "⁏", + "bsim": "∽", + "bsime": "⋍", + "bsolb": "⧅", + "bsol": "\\", + "bsolhsub": "⟈", + "bull": "•", + "bullet": "•", + "bump": "≎", + "bumpE": "⪮", + "bumpe": "≏", + "Bumpeq": "≎", + "bumpeq": "≏", + "Cacute": "Ć", + "cacute": "ć", + "capand": "⩄", + "capbrcup": "⩉", + "capcap": "⩋", + "cap": "∩", + "Cap": "⋒", + "capcup": "⩇", + "capdot": "⩀", + "CapitalDifferentialD": "ⅅ", + "caps": "∩︀", + "caret": "⁁", + "caron": "ˇ", + "Cayleys": "ℭ", + "ccaps": "⩍", + "Ccaron": "Č", + "ccaron": "č", + "Ccedil": "Ç", + "ccedil": "ç", + "Ccirc": "Ĉ", + "ccirc": "ĉ", + "Cconint": "∰", + "ccups": "⩌", + "ccupssm": "⩐", + "Cdot": "Ċ", + "cdot": "ċ", + "cedil": "¸", + "Cedilla": "¸", + "cemptyv": "⦲", + "cent": "¢", + "centerdot": "·", + "CenterDot": "·", + "cfr": "𝔠", + "Cfr": "ℭ", + "CHcy": "Ч", + "chcy": "ч", + "check": "✓", + "checkmark": "✓", + "Chi": "Χ", + "chi": "χ", + "circ": "ˆ", + "circeq": "≗", + "circlearrowleft": "↺", + "circlearrowright": "↻", + "circledast": "⊛", + "circledcirc": "⊚", + "circleddash": "⊝", + "CircleDot": "⊙", + "circledR": "®", + "circledS": "Ⓢ", + "CircleMinus": "⊖", + "CirclePlus": "⊕", + "CircleTimes": "⊗", + "cir": "○", + "cirE": "⧃", + "cire": "≗", + "cirfnint": "⨐", + "cirmid": "⫯", + "cirscir": "⧂", + "ClockwiseContourIntegral": "∲", + "CloseCurlyDoubleQuote": "”", + "CloseCurlyQuote": "’", + "clubs": "♣", + "clubsuit": "♣", + "colon": ":", + "Colon": "∷", + "Colone": "⩴", + "colone": "≔", + "coloneq": "≔", + "comma": ",", + "commat": "@", + "comp": "∁", + "compfn": "∘", + "complement": "∁", + "complexes": "ℂ", + "cong": "≅", + "congdot": "⩭", + "Congruent": "≡", + "conint": "∮", + "Conint": "∯", + "ContourIntegral": "∮", + "copf": "𝕔", + "Copf": "ℂ", + "coprod": "∐", + "Coproduct": "∐", + "copy": "©", + "COPY": "©", + "copysr": "℗", + "CounterClockwiseContourIntegral": "∳", + "crarr": "↵", + "cross": "✗", + "Cross": "⨯", + "Cscr": "𝒞", + "cscr": "𝒸", + "csub": "⫏", + "csube": "⫑", + "csup": "⫐", + "csupe": "⫒", + "ctdot": "⋯", + "cudarrl": "⤸", + "cudarrr": "⤵", + "cuepr": "⋞", + "cuesc": "⋟", + "cularr": "↶", + "cularrp": "⤽", + "cupbrcap": "⩈", + "cupcap": "⩆", + "CupCap": "≍", + "cup": "∪", + "Cup": "⋓", + "cupcup": "⩊", + "cupdot": "⊍", + "cupor": "⩅", + "cups": "∪︀", + "curarr": "↷", + "curarrm": "⤼", + "curlyeqprec": "⋞", + "curlyeqsucc": "⋟", + "curlyvee": "⋎", + "curlywedge": "⋏", + "curren": "¤", + "curvearrowleft": "↶", + "curvearrowright": "↷", + "cuvee": "⋎", + "cuwed": "⋏", + "cwconint": "∲", + "cwint": "∱", + "cylcty": "⌭", + "dagger": "†", + "Dagger": "‡", + "daleth": "ℸ", + "darr": "↓", + "Darr": "↡", + "dArr": "⇓", + "dash": "‐", + "Dashv": "⫤", + "dashv": "⊣", + "dbkarow": "⤏", + "dblac": "˝", + "Dcaron": "Ď", + "dcaron": "ď", + "Dcy": "Д", + "dcy": "д", + "ddagger": "‡", + "ddarr": "⇊", + "DD": "ⅅ", + "dd": "ⅆ", + "DDotrahd": "⤑", + "ddotseq": "⩷", + "deg": "°", + "Del": "∇", + "Delta": "Δ", + "delta": "δ", + "demptyv": "⦱", + "dfisht": "⥿", + "Dfr": "𝔇", + "dfr": "𝔡", + "dHar": "⥥", + "dharl": "⇃", + "dharr": "⇂", + "DiacriticalAcute": "´", + "DiacriticalDot": "˙", + "DiacriticalDoubleAcute": "˝", + "DiacriticalGrave": "`", + "DiacriticalTilde": "˜", + "diam": "⋄", + "diamond": "⋄", + "Diamond": "⋄", + "diamondsuit": "♦", + "diams": "♦", + "die": "¨", + "DifferentialD": "ⅆ", + "digamma": "ϝ", + "disin": "⋲", + "div": "÷", + "divide": "÷", + "divideontimes": "⋇", + "divonx": "⋇", + "DJcy": "Ђ", + "djcy": "ђ", + "dlcorn": "⌞", + "dlcrop": "⌍", + "dollar": "$", + "Dopf": "𝔻", + "dopf": "𝕕", + "Dot": "¨", + "dot": "˙", + "DotDot": "⃜", + "doteq": "≐", + "doteqdot": "≑", + "DotEqual": "≐", + "dotminus": "∸", + "dotplus": "∔", + "dotsquare": "⊡", + "doublebarwedge": "⌆", + "DoubleContourIntegral": "∯", + "DoubleDot": "¨", + "DoubleDownArrow": "⇓", + "DoubleLeftArrow": "⇐", + "DoubleLeftRightArrow": "⇔", + "DoubleLeftTee": "⫤", + "DoubleLongLeftArrow": "⟸", + "DoubleLongLeftRightArrow": "⟺", + "DoubleLongRightArrow": "⟹", + "DoubleRightArrow": "⇒", + "DoubleRightTee": "⊨", + "DoubleUpArrow": "⇑", + "DoubleUpDownArrow": "⇕", + "DoubleVerticalBar": "∥", + "DownArrowBar": "⤓", + "downarrow": "↓", + "DownArrow": "↓", + "Downarrow": "⇓", + "DownArrowUpArrow": "⇵", + "DownBreve": "̑", + "downdownarrows": "⇊", + "downharpoonleft": "⇃", + "downharpoonright": "⇂", + "DownLeftRightVector": "⥐", + "DownLeftTeeVector": "⥞", + "DownLeftVectorBar": "⥖", + "DownLeftVector": "↽", + "DownRightTeeVector": "⥟", + "DownRightVectorBar": "⥗", + "DownRightVector": "⇁", + "DownTeeArrow": "↧", + "DownTee": "⊤", + "drbkarow": "⤐", + "drcorn": "⌟", + "drcrop": "⌌", + "Dscr": "𝒟", + "dscr": "𝒹", + "DScy": "Ѕ", + "dscy": "ѕ", + "dsol": "⧶", + "Dstrok": "Đ", + "dstrok": "đ", + "dtdot": "⋱", + "dtri": "▿", + "dtrif": "▾", + "duarr": "⇵", + "duhar": "⥯", + "dwangle": "⦦", + "DZcy": "Џ", + "dzcy": "џ", + "dzigrarr": "⟿", + "Eacute": "É", + "eacute": "é", + "easter": "⩮", + "Ecaron": "Ě", + "ecaron": "ě", + "Ecirc": "Ê", + "ecirc": "ê", + "ecir": "≖", + "ecolon": "≕", + "Ecy": "Э", + "ecy": "э", + "eDDot": "⩷", + "Edot": "Ė", + "edot": "ė", + "eDot": "≑", + "ee": "ⅇ", + "efDot": "≒", + "Efr": "𝔈", + "efr": "𝔢", + "eg": "⪚", + "Egrave": "È", + "egrave": "è", + "egs": "⪖", + "egsdot": "⪘", + "el": "⪙", + "Element": "∈", + "elinters": "⏧", + "ell": "ℓ", + "els": "⪕", + "elsdot": "⪗", + "Emacr": "Ē", + "emacr": "ē", + "empty": "∅", + "emptyset": "∅", + "EmptySmallSquare": "◻", + "emptyv": "∅", + "EmptyVerySmallSquare": "▫", + "emsp13": " ", + "emsp14": " ", + "emsp": " ", + "ENG": "Ŋ", + "eng": "ŋ", + "ensp": " ", + "Eogon": "Ę", + "eogon": "ę", + "Eopf": "𝔼", + "eopf": "𝕖", + "epar": "⋕", + "eparsl": "⧣", + "eplus": "⩱", + "epsi": "ε", + "Epsilon": "Ε", + "epsilon": "ε", + "epsiv": "ϵ", + "eqcirc": "≖", + "eqcolon": "≕", + "eqsim": "≂", + "eqslantgtr": "⪖", + "eqslantless": "⪕", + "Equal": "⩵", + "equals": "=", + "EqualTilde": "≂", + "equest": "≟", + "Equilibrium": "⇌", + "equiv": "≡", + "equivDD": "⩸", + "eqvparsl": "⧥", + "erarr": "⥱", + "erDot": "≓", + "escr": "ℯ", + "Escr": "ℰ", + "esdot": "≐", + "Esim": "⩳", + "esim": "≂", + "Eta": "Η", + "eta": "η", + "ETH": "Ð", + "eth": "ð", + "Euml": "Ë", + "euml": "ë", + "euro": "€", + "excl": "!", + "exist": "∃", + "Exists": "∃", + "expectation": "ℰ", + "exponentiale": "ⅇ", + "ExponentialE": "ⅇ", + "fallingdotseq": "≒", + "Fcy": "Ф", + "fcy": "ф", + "female": "♀", + "ffilig": "ffi", + "fflig": "ff", + "ffllig": "ffl", + "Ffr": "𝔉", + "ffr": "𝔣", + "filig": "fi", + "FilledSmallSquare": "◼", + "FilledVerySmallSquare": "▪", + "fjlig": "fj", + "flat": "♭", + "fllig": "fl", + "fltns": "▱", + "fnof": "ƒ", + "Fopf": "𝔽", + "fopf": "𝕗", + "forall": "∀", + "ForAll": "∀", + "fork": "⋔", + "forkv": "⫙", + "Fouriertrf": "ℱ", + "fpartint": "⨍", + "frac12": "½", + "frac13": "⅓", + "frac14": "¼", + "frac15": "⅕", + "frac16": "⅙", + "frac18": "⅛", + "frac23": "⅔", + "frac25": "⅖", + "frac34": "¾", + "frac35": "⅗", + "frac38": "⅜", + "frac45": "⅘", + "frac56": "⅚", + "frac58": "⅝", + "frac78": "⅞", + "frasl": "⁄", + "frown": "⌢", + "fscr": "𝒻", + "Fscr": "ℱ", + "gacute": "ǵ", + "Gamma": "Γ", + "gamma": "γ", + "Gammad": "Ϝ", + "gammad": "ϝ", + "gap": "⪆", + "Gbreve": "Ğ", + "gbreve": "ğ", + "Gcedil": "Ģ", + "Gcirc": "Ĝ", + "gcirc": "ĝ", + "Gcy": "Г", + "gcy": "г", + "Gdot": "Ġ", + "gdot": "ġ", + "ge": "≥", + "gE": "≧", + "gEl": "⪌", + "gel": "⋛", + "geq": "≥", + "geqq": "≧", + "geqslant": "⩾", + "gescc": "⪩", + "ges": "⩾", + "gesdot": "⪀", + "gesdoto": "⪂", + "gesdotol": "⪄", + "gesl": "⋛︀", + "gesles": "⪔", + "Gfr": "𝔊", + "gfr": "𝔤", + "gg": "≫", + "Gg": "⋙", + "ggg": "⋙", + "gimel": "ℷ", + "GJcy": "Ѓ", + "gjcy": "ѓ", + "gla": "⪥", + "gl": "≷", + "glE": "⪒", + "glj": "⪤", + "gnap": "⪊", + "gnapprox": "⪊", + "gne": "⪈", + "gnE": "≩", + "gneq": "⪈", + "gneqq": "≩", + "gnsim": "⋧", + "Gopf": "𝔾", + "gopf": "𝕘", + "grave": "`", + "GreaterEqual": "≥", + "GreaterEqualLess": "⋛", + "GreaterFullEqual": "≧", + "GreaterGreater": "⪢", + "GreaterLess": "≷", + "GreaterSlantEqual": "⩾", + "GreaterTilde": "≳", + "Gscr": "𝒢", + "gscr": "ℊ", + "gsim": "≳", + "gsime": "⪎", + "gsiml": "⪐", + "gtcc": "⪧", + "gtcir": "⩺", + "gt": ">", + "GT": ">", + "Gt": "≫", + "gtdot": "⋗", + "gtlPar": "⦕", + "gtquest": "⩼", + "gtrapprox": "⪆", + "gtrarr": "⥸", + "gtrdot": "⋗", + "gtreqless": "⋛", + "gtreqqless": "⪌", + "gtrless": "≷", + "gtrsim": "≳", + "gvertneqq": "≩︀", + "gvnE": "≩︀", + "Hacek": "ˇ", + "hairsp": " ", + "half": "½", + "hamilt": "ℋ", + "HARDcy": "Ъ", + "hardcy": "ъ", + "harrcir": "⥈", + "harr": "↔", + "hArr": "⇔", + "harrw": "↭", + "Hat": "^", + "hbar": "ℏ", + "Hcirc": "Ĥ", + "hcirc": "ĥ", + "hearts": "♥", + "heartsuit": "♥", + "hellip": "…", + "hercon": "⊹", + "hfr": "𝔥", + "Hfr": "ℌ", + "HilbertSpace": "ℋ", + "hksearow": "⤥", + "hkswarow": "⤦", + "hoarr": "⇿", + "homtht": "∻", + "hookleftarrow": "↩", + "hookrightarrow": "↪", + "hopf": "𝕙", + "Hopf": "ℍ", + "horbar": "―", + "HorizontalLine": "─", + "hscr": "𝒽", + "Hscr": "ℋ", + "hslash": "ℏ", + "Hstrok": "Ħ", + "hstrok": "ħ", + "HumpDownHump": "≎", + "HumpEqual": "≏", + "hybull": "⁃", + "hyphen": "‐", + "Iacute": "Í", + "iacute": "í", + "ic": "⁣", + "Icirc": "Î", + "icirc": "î", + "Icy": "И", + "icy": "и", + "Idot": "İ", + "IEcy": "Е", + "iecy": "е", + "iexcl": "¡", + "iff": "⇔", + "ifr": "𝔦", + "Ifr": "ℑ", + "Igrave": "Ì", + "igrave": "ì", + "ii": "ⅈ", + "iiiint": "⨌", + "iiint": "∭", + "iinfin": "⧜", + "iiota": "℩", + "IJlig": "IJ", + "ijlig": "ij", + "Imacr": "Ī", + "imacr": "ī", + "image": "ℑ", + "ImaginaryI": "ⅈ", + "imagline": "ℐ", + "imagpart": "ℑ", + "imath": "ı", + "Im": "ℑ", + "imof": "⊷", + "imped": "Ƶ", + "Implies": "⇒", + "incare": "℅", + "in": "∈", + "infin": "∞", + "infintie": "⧝", + "inodot": "ı", + "intcal": "⊺", + "int": "∫", + "Int": "∬", + "integers": "ℤ", + "Integral": "∫", + "intercal": "⊺", + "Intersection": "⋂", + "intlarhk": "⨗", + "intprod": "⨼", + "InvisibleComma": "⁣", + "InvisibleTimes": "⁢", + "IOcy": "Ё", + "iocy": "ё", + "Iogon": "Į", + "iogon": "į", + "Iopf": "𝕀", + "iopf": "𝕚", + "Iota": "Ι", + "iota": "ι", + "iprod": "⨼", + "iquest": "¿", + "iscr": "𝒾", + "Iscr": "ℐ", + "isin": "∈", + "isindot": "⋵", + "isinE": "⋹", + "isins": "⋴", + "isinsv": "⋳", + "isinv": "∈", + "it": "⁢", + "Itilde": "Ĩ", + "itilde": "ĩ", + "Iukcy": "І", + "iukcy": "і", + "Iuml": "Ï", + "iuml": "ï", + "Jcirc": "Ĵ", + "jcirc": "ĵ", + "Jcy": "Й", + "jcy": "й", + "Jfr": "𝔍", + "jfr": "𝔧", + "jmath": "ȷ", + "Jopf": "𝕁", + "jopf": "𝕛", + "Jscr": "𝒥", + "jscr": "𝒿", + "Jsercy": "Ј", + "jsercy": "ј", + "Jukcy": "Є", + "jukcy": "є", + "Kappa": "Κ", + "kappa": "κ", + "kappav": "ϰ", + "Kcedil": "Ķ", + "kcedil": "ķ", + "Kcy": "К", + "kcy": "к", + "Kfr": "𝔎", + "kfr": "𝔨", + "kgreen": "ĸ", + "KHcy": "Х", + "khcy": "х", + "KJcy": "Ќ", + "kjcy": "ќ", + "Kopf": "𝕂", + "kopf": "𝕜", + "Kscr": "𝒦", + "kscr": "𝓀", + "lAarr": "⇚", + "Lacute": "Ĺ", + "lacute": "ĺ", + "laemptyv": "⦴", + "lagran": "ℒ", + "Lambda": "Λ", + "lambda": "λ", + "lang": "⟨", + "Lang": "⟪", + "langd": "⦑", + "langle": "⟨", + "lap": "⪅", + "Laplacetrf": "ℒ", + "laquo": "«", + "larrb": "⇤", + "larrbfs": "⤟", + "larr": "←", + "Larr": "↞", + "lArr": "⇐", + "larrfs": "⤝", + "larrhk": "↩", + "larrlp": "↫", + "larrpl": "⤹", + "larrsim": "⥳", + "larrtl": "↢", + "latail": "⤙", + "lAtail": "⤛", + "lat": "⪫", + "late": "⪭", + "lates": "⪭︀", + "lbarr": "⤌", + "lBarr": "⤎", + "lbbrk": "❲", + "lbrace": "{", + "lbrack": "[", + "lbrke": "⦋", + "lbrksld": "⦏", + "lbrkslu": "⦍", + "Lcaron": "Ľ", + "lcaron": "ľ", + "Lcedil": "Ļ", + "lcedil": "ļ", + "lceil": "⌈", + "lcub": "{", + "Lcy": "Л", + "lcy": "л", + "ldca": "⤶", + "ldquo": "“", + "ldquor": "„", + "ldrdhar": "⥧", + "ldrushar": "⥋", + "ldsh": "↲", + "le": "≤", + "lE": "≦", + "LeftAngleBracket": "⟨", + "LeftArrowBar": "⇤", + "leftarrow": "←", + "LeftArrow": "←", + "Leftarrow": "⇐", + "LeftArrowRightArrow": "⇆", + "leftarrowtail": "↢", + "LeftCeiling": "⌈", + "LeftDoubleBracket": "⟦", + "LeftDownTeeVector": "⥡", + "LeftDownVectorBar": "⥙", + "LeftDownVector": "⇃", + "LeftFloor": "⌊", + "leftharpoondown": "↽", + "leftharpoonup": "↼", + "leftleftarrows": "⇇", + "leftrightarrow": "↔", + "LeftRightArrow": "↔", + "Leftrightarrow": "⇔", + "leftrightarrows": "⇆", + "leftrightharpoons": "⇋", + "leftrightsquigarrow": "↭", + "LeftRightVector": "⥎", + "LeftTeeArrow": "↤", + "LeftTee": "⊣", + "LeftTeeVector": "⥚", + "leftthreetimes": "⋋", + "LeftTriangleBar": "⧏", + "LeftTriangle": "⊲", + "LeftTriangleEqual": "⊴", + "LeftUpDownVector": "⥑", + "LeftUpTeeVector": "⥠", + "LeftUpVectorBar": "⥘", + "LeftUpVector": "↿", + "LeftVectorBar": "⥒", + "LeftVector": "↼", + "lEg": "⪋", + "leg": "⋚", + "leq": "≤", + "leqq": "≦", + "leqslant": "⩽", + "lescc": "⪨", + "les": "⩽", + "lesdot": "⩿", + "lesdoto": "⪁", + "lesdotor": "⪃", + "lesg": "⋚︀", + "lesges": "⪓", + "lessapprox": "⪅", + "lessdot": "⋖", + "lesseqgtr": "⋚", + "lesseqqgtr": "⪋", + "LessEqualGreater": "⋚", + "LessFullEqual": "≦", + "LessGreater": "≶", + "lessgtr": "≶", + "LessLess": "⪡", + "lesssim": "≲", + "LessSlantEqual": "⩽", + "LessTilde": "≲", + "lfisht": "⥼", + "lfloor": "⌊", + "Lfr": "𝔏", + "lfr": "𝔩", + "lg": "≶", + "lgE": "⪑", + "lHar": "⥢", + "lhard": "↽", + "lharu": "↼", + "lharul": "⥪", + "lhblk": "▄", + "LJcy": "Љ", + "ljcy": "љ", + "llarr": "⇇", + "ll": "≪", + "Ll": "⋘", + "llcorner": "⌞", + "Lleftarrow": "⇚", + "llhard": "⥫", + "lltri": "◺", + "Lmidot": "Ŀ", + "lmidot": "ŀ", + "lmoustache": "⎰", + "lmoust": "⎰", + "lnap": "⪉", + "lnapprox": "⪉", + "lne": "⪇", + "lnE": "≨", + "lneq": "⪇", + "lneqq": "≨", + "lnsim": "⋦", + "loang": "⟬", + "loarr": "⇽", + "lobrk": "⟦", + "longleftarrow": "⟵", + "LongLeftArrow": "⟵", + "Longleftarrow": "⟸", + "longleftrightarrow": "⟷", + "LongLeftRightArrow": "⟷", + "Longleftrightarrow": "⟺", + "longmapsto": "⟼", + "longrightarrow": "⟶", + "LongRightArrow": "⟶", + "Longrightarrow": "⟹", + "looparrowleft": "↫", + "looparrowright": "↬", + "lopar": "⦅", + "Lopf": "𝕃", + "lopf": "𝕝", + "loplus": "⨭", + "lotimes": "⨴", + "lowast": "∗", + "lowbar": "_", + "LowerLeftArrow": "↙", + "LowerRightArrow": "↘", + "loz": "◊", + "lozenge": "◊", + "lozf": "⧫", + "lpar": "(", + "lparlt": "⦓", + "lrarr": "⇆", + "lrcorner": "⌟", + "lrhar": "⇋", + "lrhard": "⥭", + "lrm": "‎", + "lrtri": "⊿", + "lsaquo": "‹", + "lscr": "𝓁", + "Lscr": "ℒ", + "lsh": "↰", + "Lsh": "↰", + "lsim": "≲", + "lsime": "⪍", + "lsimg": "⪏", + "lsqb": "[", + "lsquo": "‘", + "lsquor": "‚", + "Lstrok": "Ł", + "lstrok": "ł", + "ltcc": "⪦", + "ltcir": "⩹", + "lt": "<", + "LT": "<", + "Lt": "≪", + "ltdot": "⋖", + "lthree": "⋋", + "ltimes": "⋉", + "ltlarr": "⥶", + "ltquest": "⩻", + "ltri": "◃", + "ltrie": "⊴", + "ltrif": "◂", + "ltrPar": "⦖", + "lurdshar": "⥊", + "luruhar": "⥦", + "lvertneqq": "≨︀", + "lvnE": "≨︀", + "macr": "¯", + "male": "♂", + "malt": "✠", + "maltese": "✠", + "Map": "⤅", + "map": "↦", + "mapsto": "↦", + "mapstodown": "↧", + "mapstoleft": "↤", + "mapstoup": "↥", + "marker": "▮", + "mcomma": "⨩", + "Mcy": "М", + "mcy": "м", + "mdash": "—", + "mDDot": "∺", + "measuredangle": "∡", + "MediumSpace": " ", + "Mellintrf": "ℳ", + "Mfr": "𝔐", + "mfr": "𝔪", + "mho": "℧", + "micro": "µ", + "midast": "*", + "midcir": "⫰", + "mid": "∣", + "middot": "·", + "minusb": "⊟", + "minus": "−", + "minusd": "∸", + "minusdu": "⨪", + "MinusPlus": "∓", + "mlcp": "⫛", + "mldr": "…", + "mnplus": "∓", + "models": "⊧", + "Mopf": "𝕄", + "mopf": "𝕞", + "mp": "∓", + "mscr": "𝓂", + "Mscr": "ℳ", + "mstpos": "∾", + "Mu": "Μ", + "mu": "μ", + "multimap": "⊸", + "mumap": "⊸", + "nabla": "∇", + "Nacute": "Ń", + "nacute": "ń", + "nang": "∠⃒", + "nap": "≉", + "napE": "⩰̸", + "napid": "≋̸", + "napos": "ʼn", + "napprox": "≉", + "natural": "♮", + "naturals": "ℕ", + "natur": "♮", + "nbsp": " ", + "nbump": "≎̸", + "nbumpe": "≏̸", + "ncap": "⩃", + "Ncaron": "Ň", + "ncaron": "ň", + "Ncedil": "Ņ", + "ncedil": "ņ", + "ncong": "≇", + "ncongdot": "⩭̸", + "ncup": "⩂", + "Ncy": "Н", + "ncy": "н", + "ndash": "–", + "nearhk": "⤤", + "nearr": "↗", + "neArr": "⇗", + "nearrow": "↗", + "ne": "≠", + "nedot": "≐̸", + "NegativeMediumSpace": "​", + "NegativeThickSpace": "​", + "NegativeThinSpace": "​", + "NegativeVeryThinSpace": "​", + "nequiv": "≢", + "nesear": "⤨", + "nesim": "≂̸", + "NestedGreaterGreater": "≫", + "NestedLessLess": "≪", + "NewLine": "\n", + "nexist": "∄", + "nexists": "∄", + "Nfr": "𝔑", + "nfr": "𝔫", + "ngE": "≧̸", + "nge": "≱", + "ngeq": "≱", + "ngeqq": "≧̸", + "ngeqslant": "⩾̸", + "nges": "⩾̸", + "nGg": "⋙̸", + "ngsim": "≵", + "nGt": "≫⃒", + "ngt": "≯", + "ngtr": "≯", + "nGtv": "≫̸", + "nharr": "↮", + "nhArr": "⇎", + "nhpar": "⫲", + "ni": "∋", + "nis": "⋼", + "nisd": "⋺", + "niv": "∋", + "NJcy": "Њ", + "njcy": "њ", + "nlarr": "↚", + "nlArr": "⇍", + "nldr": "‥", + "nlE": "≦̸", + "nle": "≰", + "nleftarrow": "↚", + "nLeftarrow": "⇍", + "nleftrightarrow": "↮", + "nLeftrightarrow": "⇎", + "nleq": "≰", + "nleqq": "≦̸", + "nleqslant": "⩽̸", + "nles": "⩽̸", + "nless": "≮", + "nLl": "⋘̸", + "nlsim": "≴", + "nLt": "≪⃒", + "nlt": "≮", + "nltri": "⋪", + "nltrie": "⋬", + "nLtv": "≪̸", + "nmid": "∤", + "NoBreak": "⁠", + "NonBreakingSpace": " ", + "nopf": "𝕟", + "Nopf": "ℕ", + "Not": "⫬", + "not": "¬", + "NotCongruent": "≢", + "NotCupCap": "≭", + "NotDoubleVerticalBar": "∦", + "NotElement": "∉", + "NotEqual": "≠", + "NotEqualTilde": "≂̸", + "NotExists": "∄", + "NotGreater": "≯", + "NotGreaterEqual": "≱", + "NotGreaterFullEqual": "≧̸", + "NotGreaterGreater": "≫̸", + "NotGreaterLess": "≹", + "NotGreaterSlantEqual": "⩾̸", + "NotGreaterTilde": "≵", + "NotHumpDownHump": "≎̸", + "NotHumpEqual": "≏̸", + "notin": "∉", + "notindot": "⋵̸", + "notinE": "⋹̸", + "notinva": "∉", + "notinvb": "⋷", + "notinvc": "⋶", + "NotLeftTriangleBar": "⧏̸", + "NotLeftTriangle": "⋪", + "NotLeftTriangleEqual": "⋬", + "NotLess": "≮", + "NotLessEqual": "≰", + "NotLessGreater": "≸", + "NotLessLess": "≪̸", + "NotLessSlantEqual": "⩽̸", + "NotLessTilde": "≴", + "NotNestedGreaterGreater": "⪢̸", + "NotNestedLessLess": "⪡̸", + "notni": "∌", + "notniva": "∌", + "notnivb": "⋾", + "notnivc": "⋽", + "NotPrecedes": "⊀", + "NotPrecedesEqual": "⪯̸", + "NotPrecedesSlantEqual": "⋠", + "NotReverseElement": "∌", + "NotRightTriangleBar": "⧐̸", + "NotRightTriangle": "⋫", + "NotRightTriangleEqual": "⋭", + "NotSquareSubset": "⊏̸", + "NotSquareSubsetEqual": "⋢", + "NotSquareSuperset": "⊐̸", + "NotSquareSupersetEqual": "⋣", + "NotSubset": "⊂⃒", + "NotSubsetEqual": "⊈", + "NotSucceeds": "⊁", + "NotSucceedsEqual": "⪰̸", + "NotSucceedsSlantEqual": "⋡", + "NotSucceedsTilde": "≿̸", + "NotSuperset": "⊃⃒", + "NotSupersetEqual": "⊉", + "NotTilde": "≁", + "NotTildeEqual": "≄", + "NotTildeFullEqual": "≇", + "NotTildeTilde": "≉", + "NotVerticalBar": "∤", + "nparallel": "∦", + "npar": "∦", + "nparsl": "⫽⃥", + "npart": "∂̸", + "npolint": "⨔", + "npr": "⊀", + "nprcue": "⋠", + "nprec": "⊀", + "npreceq": "⪯̸", + "npre": "⪯̸", + "nrarrc": "⤳̸", + "nrarr": "↛", + "nrArr": "⇏", + "nrarrw": "↝̸", + "nrightarrow": "↛", + "nRightarrow": "⇏", + "nrtri": "⋫", + "nrtrie": "⋭", + "nsc": "⊁", + "nsccue": "⋡", + "nsce": "⪰̸", + "Nscr": "𝒩", + "nscr": "𝓃", + "nshortmid": "∤", + "nshortparallel": "∦", + "nsim": "≁", + "nsime": "≄", + "nsimeq": "≄", + "nsmid": "∤", + "nspar": "∦", + "nsqsube": "⋢", + "nsqsupe": "⋣", + "nsub": "⊄", + "nsubE": "⫅̸", + "nsube": "⊈", + "nsubset": "⊂⃒", + "nsubseteq": "⊈", + "nsubseteqq": "⫅̸", + "nsucc": "⊁", + "nsucceq": "⪰̸", + "nsup": "⊅", + "nsupE": "⫆̸", + "nsupe": "⊉", + "nsupset": "⊃⃒", + "nsupseteq": "⊉", + "nsupseteqq": "⫆̸", + "ntgl": "≹", + "Ntilde": "Ñ", + "ntilde": "ñ", + "ntlg": "≸", + "ntriangleleft": "⋪", + "ntrianglelefteq": "⋬", + "ntriangleright": "⋫", + "ntrianglerighteq": "⋭", + "Nu": "Ν", + "nu": "ν", + "num": "#", + "numero": "№", + "numsp": " ", + "nvap": "≍⃒", + "nvdash": "⊬", + "nvDash": "⊭", + "nVdash": "⊮", + "nVDash": "⊯", + "nvge": "≥⃒", + "nvgt": ">⃒", + "nvHarr": "⤄", + "nvinfin": "⧞", + "nvlArr": "⤂", + "nvle": "≤⃒", + "nvlt": "<⃒", + "nvltrie": "⊴⃒", + "nvrArr": "⤃", + "nvrtrie": "⊵⃒", + "nvsim": "∼⃒", + "nwarhk": "⤣", + "nwarr": "↖", + "nwArr": "⇖", + "nwarrow": "↖", + "nwnear": "⤧", + "Oacute": "Ó", + "oacute": "ó", + "oast": "⊛", + "Ocirc": "Ô", + "ocirc": "ô", + "ocir": "⊚", + "Ocy": "О", + "ocy": "о", + "odash": "⊝", + "Odblac": "Ő", + "odblac": "ő", + "odiv": "⨸", + "odot": "⊙", + "odsold": "⦼", + "OElig": "Œ", + "oelig": "œ", + "ofcir": "⦿", + "Ofr": "𝔒", + "ofr": "𝔬", + "ogon": "˛", + "Ograve": "Ò", + "ograve": "ò", + "ogt": "⧁", + "ohbar": "⦵", + "ohm": "Ω", + "oint": "∮", + "olarr": "↺", + "olcir": "⦾", + "olcross": "⦻", + "oline": "‾", + "olt": "⧀", + "Omacr": "Ō", + "omacr": "ō", + "Omega": "Ω", + "omega": "ω", + "Omicron": "Ο", + "omicron": "ο", + "omid": "⦶", + "ominus": "⊖", + "Oopf": "𝕆", + "oopf": "𝕠", + "opar": "⦷", + "OpenCurlyDoubleQuote": "“", + "OpenCurlyQuote": "‘", + "operp": "⦹", + "oplus": "⊕", + "orarr": "↻", + "Or": "⩔", + "or": "∨", + "ord": "⩝", + "order": "ℴ", + "orderof": "ℴ", + "ordf": "ª", + "ordm": "º", + "origof": "⊶", + "oror": "⩖", + "orslope": "⩗", + "orv": "⩛", + "oS": "Ⓢ", + "Oscr": "𝒪", + "oscr": "ℴ", + "Oslash": "Ø", + "oslash": "ø", + "osol": "⊘", + "Otilde": "Õ", + "otilde": "õ", + "otimesas": "⨶", + "Otimes": "⨷", + "otimes": "⊗", + "Ouml": "Ö", + "ouml": "ö", + "ovbar": "⌽", + "OverBar": "‾", + "OverBrace": "⏞", + "OverBracket": "⎴", + "OverParenthesis": "⏜", + "para": "¶", + "parallel": "∥", + "par": "∥", + "parsim": "⫳", + "parsl": "⫽", + "part": "∂", + "PartialD": "∂", + "Pcy": "П", + "pcy": "п", + "percnt": "%", + "period": ".", + "permil": "‰", + "perp": "⊥", + "pertenk": "‱", + "Pfr": "𝔓", + "pfr": "𝔭", + "Phi": "Φ", + "phi": "φ", + "phiv": "ϕ", + "phmmat": "ℳ", + "phone": "☎", + "Pi": "Π", + "pi": "π", + "pitchfork": "⋔", + "piv": "ϖ", + "planck": "ℏ", + "planckh": "ℎ", + "plankv": "ℏ", + "plusacir": "⨣", + "plusb": "⊞", + "pluscir": "⨢", + "plus": "+", + "plusdo": "∔", + "plusdu": "⨥", + "pluse": "⩲", + "PlusMinus": "±", + "plusmn": "±", + "plussim": "⨦", + "plustwo": "⨧", + "pm": "±", + "Poincareplane": "ℌ", + "pointint": "⨕", + "popf": "𝕡", + "Popf": "ℙ", + "pound": "£", + "prap": "⪷", + "Pr": "⪻", + "pr": "≺", + "prcue": "≼", + "precapprox": "⪷", + "prec": "≺", + "preccurlyeq": "≼", + "Precedes": "≺", + "PrecedesEqual": "⪯", + "PrecedesSlantEqual": "≼", + "PrecedesTilde": "≾", + "preceq": "⪯", + "precnapprox": "⪹", + "precneqq": "⪵", + "precnsim": "⋨", + "pre": "⪯", + "prE": "⪳", + "precsim": "≾", + "prime": "′", + "Prime": "″", + "primes": "ℙ", + "prnap": "⪹", + "prnE": "⪵", + "prnsim": "⋨", + "prod": "∏", + "Product": "∏", + "profalar": "⌮", + "profline": "⌒", + "profsurf": "⌓", + "prop": "∝", + "Proportional": "∝", + "Proportion": "∷", + "propto": "∝", + "prsim": "≾", + "prurel": "⊰", + "Pscr": "𝒫", + "pscr": "𝓅", + "Psi": "Ψ", + "psi": "ψ", + "puncsp": " ", + "Qfr": "𝔔", + "qfr": "𝔮", + "qint": "⨌", + "qopf": "𝕢", + "Qopf": "ℚ", + "qprime": "⁗", + "Qscr": "𝒬", + "qscr": "𝓆", + "quaternions": "ℍ", + "quatint": "⨖", + "quest": "?", + "questeq": "≟", + "quot": "\"", + "QUOT": "\"", + "rAarr": "⇛", + "race": "∽̱", + "Racute": "Ŕ", + "racute": "ŕ", + "radic": "√", + "raemptyv": "⦳", + "rang": "⟩", + "Rang": "⟫", + "rangd": "⦒", + "range": "⦥", + "rangle": "⟩", + "raquo": "»", + "rarrap": "⥵", + "rarrb": "⇥", + "rarrbfs": "⤠", + "rarrc": "⤳", + "rarr": "→", + "Rarr": "↠", + "rArr": "⇒", + "rarrfs": "⤞", + "rarrhk": "↪", + "rarrlp": "↬", + "rarrpl": "⥅", + "rarrsim": "⥴", + "Rarrtl": "⤖", + "rarrtl": "↣", + "rarrw": "↝", + "ratail": "⤚", + "rAtail": "⤜", + "ratio": "∶", + "rationals": "ℚ", + "rbarr": "⤍", + "rBarr": "⤏", + "RBarr": "⤐", + "rbbrk": "❳", + "rbrace": "}", + "rbrack": "]", + "rbrke": "⦌", + "rbrksld": "⦎", + "rbrkslu": "⦐", + "Rcaron": "Ř", + "rcaron": "ř", + "Rcedil": "Ŗ", + "rcedil": "ŗ", + "rceil": "⌉", + "rcub": "}", + "Rcy": "Р", + "rcy": "р", + "rdca": "⤷", + "rdldhar": "⥩", + "rdquo": "”", + "rdquor": "”", + "rdsh": "↳", + "real": "ℜ", + "realine": "ℛ", + "realpart": "ℜ", + "reals": "ℝ", + "Re": "ℜ", + "rect": "▭", + "reg": "®", + "REG": "®", + "ReverseElement": "∋", + "ReverseEquilibrium": "⇋", + "ReverseUpEquilibrium": "⥯", + "rfisht": "⥽", + "rfloor": "⌋", + "rfr": "𝔯", + "Rfr": "ℜ", + "rHar": "⥤", + "rhard": "⇁", + "rharu": "⇀", + "rharul": "⥬", + "Rho": "Ρ", + "rho": "ρ", + "rhov": "ϱ", + "RightAngleBracket": "⟩", + "RightArrowBar": "⇥", + "rightarrow": "→", + "RightArrow": "→", + "Rightarrow": "⇒", + "RightArrowLeftArrow": "⇄", + "rightarrowtail": "↣", + "RightCeiling": "⌉", + "RightDoubleBracket": "⟧", + "RightDownTeeVector": "⥝", + "RightDownVectorBar": "⥕", + "RightDownVector": "⇂", + "RightFloor": "⌋", + "rightharpoondown": "⇁", + "rightharpoonup": "⇀", + "rightleftarrows": "⇄", + "rightleftharpoons": "⇌", + "rightrightarrows": "⇉", + "rightsquigarrow": "↝", + "RightTeeArrow": "↦", + "RightTee": "⊢", + "RightTeeVector": "⥛", + "rightthreetimes": "⋌", + "RightTriangleBar": "⧐", + "RightTriangle": "⊳", + "RightTriangleEqual": "⊵", + "RightUpDownVector": "⥏", + "RightUpTeeVector": "⥜", + "RightUpVectorBar": "⥔", + "RightUpVector": "↾", + "RightVectorBar": "⥓", + "RightVector": "⇀", + "ring": "˚", + "risingdotseq": "≓", + "rlarr": "⇄", + "rlhar": "⇌", + "rlm": "‏", + "rmoustache": "⎱", + "rmoust": "⎱", + "rnmid": "⫮", + "roang": "⟭", + "roarr": "⇾", + "robrk": "⟧", + "ropar": "⦆", + "ropf": "𝕣", + "Ropf": "ℝ", + "roplus": "⨮", + "rotimes": "⨵", + "RoundImplies": "⥰", + "rpar": ")", + "rpargt": "⦔", + "rppolint": "⨒", + "rrarr": "⇉", + "Rrightarrow": "⇛", + "rsaquo": "›", + "rscr": "𝓇", + "Rscr": "ℛ", + "rsh": "↱", + "Rsh": "↱", + "rsqb": "]", + "rsquo": "’", + "rsquor": "’", + "rthree": "⋌", + "rtimes": "⋊", + "rtri": "▹", + "rtrie": "⊵", + "rtrif": "▸", + "rtriltri": "⧎", + "RuleDelayed": "⧴", + "ruluhar": "⥨", + "rx": "℞", + "Sacute": "Ś", + "sacute": "ś", + "sbquo": "‚", + "scap": "⪸", + "Scaron": "Š", + "scaron": "š", + "Sc": "⪼", + "sc": "≻", + "sccue": "≽", + "sce": "⪰", + "scE": "⪴", + "Scedil": "Ş", + "scedil": "ş", + "Scirc": "Ŝ", + "scirc": "ŝ", + "scnap": "⪺", + "scnE": "⪶", + "scnsim": "⋩", + "scpolint": "⨓", + "scsim": "≿", + "Scy": "С", + "scy": "с", + "sdotb": "⊡", + "sdot": "⋅", + "sdote": "⩦", + "searhk": "⤥", + "searr": "↘", + "seArr": "⇘", + "searrow": "↘", + "sect": "§", + "semi": ";", + "seswar": "⤩", + "setminus": "∖", + "setmn": "∖", + "sext": "✶", + "Sfr": "𝔖", + "sfr": "𝔰", + "sfrown": "⌢", + "sharp": "♯", + "SHCHcy": "Щ", + "shchcy": "щ", + "SHcy": "Ш", + "shcy": "ш", + "ShortDownArrow": "↓", + "ShortLeftArrow": "←", + "shortmid": "∣", + "shortparallel": "∥", + "ShortRightArrow": "→", + "ShortUpArrow": "↑", + "shy": "­", + "Sigma": "Σ", + "sigma": "σ", + "sigmaf": "ς", + "sigmav": "ς", + "sim": "∼", + "simdot": "⩪", + "sime": "≃", + "simeq": "≃", + "simg": "⪞", + "simgE": "⪠", + "siml": "⪝", + "simlE": "⪟", + "simne": "≆", + "simplus": "⨤", + "simrarr": "⥲", + "slarr": "←", + "SmallCircle": "∘", + "smallsetminus": "∖", + "smashp": "⨳", + "smeparsl": "⧤", + "smid": "∣", + "smile": "⌣", + "smt": "⪪", + "smte": "⪬", + "smtes": "⪬︀", + "SOFTcy": "Ь", + "softcy": "ь", + "solbar": "⌿", + "solb": "⧄", + "sol": "/", + "Sopf": "𝕊", + "sopf": "𝕤", + "spades": "♠", + "spadesuit": "♠", + "spar": "∥", + "sqcap": "⊓", + "sqcaps": "⊓︀", + "sqcup": "⊔", + "sqcups": "⊔︀", + "Sqrt": "√", + "sqsub": "⊏", + "sqsube": "⊑", + "sqsubset": "⊏", + "sqsubseteq": "⊑", + "sqsup": "⊐", + "sqsupe": "⊒", + "sqsupset": "⊐", + "sqsupseteq": "⊒", + "square": "□", + "Square": "□", + "SquareIntersection": "⊓", + "SquareSubset": "⊏", + "SquareSubsetEqual": "⊑", + "SquareSuperset": "⊐", + "SquareSupersetEqual": "⊒", + "SquareUnion": "⊔", + "squarf": "▪", + "squ": "□", + "squf": "▪", + "srarr": "→", + "Sscr": "𝒮", + "sscr": "𝓈", + "ssetmn": "∖", + "ssmile": "⌣", + "sstarf": "⋆", + "Star": "⋆", + "star": "☆", + "starf": "★", + "straightepsilon": "ϵ", + "straightphi": "ϕ", + "strns": "¯", + "sub": "⊂", + "Sub": "⋐", + "subdot": "⪽", + "subE": "⫅", + "sube": "⊆", + "subedot": "⫃", + "submult": "⫁", + "subnE": "⫋", + "subne": "⊊", + "subplus": "⪿", + "subrarr": "⥹", + "subset": "⊂", + "Subset": "⋐", + "subseteq": "⊆", + "subseteqq": "⫅", + "SubsetEqual": "⊆", + "subsetneq": "⊊", + "subsetneqq": "⫋", + "subsim": "⫇", + "subsub": "⫕", + "subsup": "⫓", + "succapprox": "⪸", + "succ": "≻", + "succcurlyeq": "≽", + "Succeeds": "≻", + "SucceedsEqual": "⪰", + "SucceedsSlantEqual": "≽", + "SucceedsTilde": "≿", + "succeq": "⪰", + "succnapprox": "⪺", + "succneqq": "⪶", + "succnsim": "⋩", + "succsim": "≿", + "SuchThat": "∋", + "sum": "∑", + "Sum": "∑", + "sung": "♪", + "sup1": "¹", + "sup2": "²", + "sup3": "³", + "sup": "⊃", + "Sup": "⋑", + "supdot": "⪾", + "supdsub": "⫘", + "supE": "⫆", + "supe": "⊇", + "supedot": "⫄", + "Superset": "⊃", + "SupersetEqual": "⊇", + "suphsol": "⟉", + "suphsub": "⫗", + "suplarr": "⥻", + "supmult": "⫂", + "supnE": "⫌", + "supne": "⊋", + "supplus": "⫀", + "supset": "⊃", + "Supset": "⋑", + "supseteq": "⊇", + "supseteqq": "⫆", + "supsetneq": "⊋", + "supsetneqq": "⫌", + "supsim": "⫈", + "supsub": "⫔", + "supsup": "⫖", + "swarhk": "⤦", + "swarr": "↙", + "swArr": "⇙", + "swarrow": "↙", + "swnwar": "⤪", + "szlig": "ß", + "Tab": "\t", + "target": "⌖", + "Tau": "Τ", + "tau": "τ", + "tbrk": "⎴", + "Tcaron": "Ť", + "tcaron": "ť", + "Tcedil": "Ţ", + "tcedil": "ţ", + "Tcy": "Т", + "tcy": "т", + "tdot": "⃛", + "telrec": "⌕", + "Tfr": "𝔗", + "tfr": "𝔱", + "there4": "∴", + "therefore": "∴", + "Therefore": "∴", + "Theta": "Θ", + "theta": "θ", + "thetasym": "ϑ", + "thetav": "ϑ", + "thickapprox": "≈", + "thicksim": "∼", + "ThickSpace": "  ", + "ThinSpace": " ", + "thinsp": " ", + "thkap": "≈", + "thksim": "∼", + "THORN": "Þ", + "thorn": "þ", + "tilde": "˜", + "Tilde": "∼", + "TildeEqual": "≃", + "TildeFullEqual": "≅", + "TildeTilde": "≈", + "timesbar": "⨱", + "timesb": "⊠", + "times": "×", + "timesd": "⨰", + "tint": "∭", + "toea": "⤨", + "topbot": "⌶", + "topcir": "⫱", + "top": "⊤", + "Topf": "𝕋", + "topf": "𝕥", + "topfork": "⫚", + "tosa": "⤩", + "tprime": "‴", + "trade": "™", + "TRADE": "™", + "triangle": "▵", + "triangledown": "▿", + "triangleleft": "◃", + "trianglelefteq": "⊴", + "triangleq": "≜", + "triangleright": "▹", + "trianglerighteq": "⊵", + "tridot": "◬", + "trie": "≜", + "triminus": "⨺", + "TripleDot": "⃛", + "triplus": "⨹", + "trisb": "⧍", + "tritime": "⨻", + "trpezium": "⏢", + "Tscr": "𝒯", + "tscr": "𝓉", + "TScy": "Ц", + "tscy": "ц", + "TSHcy": "Ћ", + "tshcy": "ћ", + "Tstrok": "Ŧ", + "tstrok": "ŧ", + "twixt": "≬", + "twoheadleftarrow": "↞", + "twoheadrightarrow": "↠", + "Uacute": "Ú", + "uacute": "ú", + "uarr": "↑", + "Uarr": "↟", + "uArr": "⇑", + "Uarrocir": "⥉", + "Ubrcy": "Ў", + "ubrcy": "ў", + "Ubreve": "Ŭ", + "ubreve": "ŭ", + "Ucirc": "Û", + "ucirc": "û", + "Ucy": "У", + "ucy": "у", + "udarr": "⇅", + "Udblac": "Ű", + "udblac": "ű", + "udhar": "⥮", + "ufisht": "⥾", + "Ufr": "𝔘", + "ufr": "𝔲", + "Ugrave": "Ù", + "ugrave": "ù", + "uHar": "⥣", + "uharl": "↿", + "uharr": "↾", + "uhblk": "▀", + "ulcorn": "⌜", + "ulcorner": "⌜", + "ulcrop": "⌏", + "ultri": "◸", + "Umacr": "Ū", + "umacr": "ū", + "uml": "¨", + "UnderBar": "_", + "UnderBrace": "⏟", + "UnderBracket": "⎵", + "UnderParenthesis": "⏝", + "Union": "⋃", + "UnionPlus": "⊎", + "Uogon": "Ų", + "uogon": "ų", + "Uopf": "𝕌", + "uopf": "𝕦", + "UpArrowBar": "⤒", + "uparrow": "↑", + "UpArrow": "↑", + "Uparrow": "⇑", + "UpArrowDownArrow": "⇅", + "updownarrow": "↕", + "UpDownArrow": "↕", + "Updownarrow": "⇕", + "UpEquilibrium": "⥮", + "upharpoonleft": "↿", + "upharpoonright": "↾", + "uplus": "⊎", + "UpperLeftArrow": "↖", + "UpperRightArrow": "↗", + "upsi": "υ", + "Upsi": "ϒ", + "upsih": "ϒ", + "Upsilon": "Υ", + "upsilon": "υ", + "UpTeeArrow": "↥", + "UpTee": "⊥", + "upuparrows": "⇈", + "urcorn": "⌝", + "urcorner": "⌝", + "urcrop": "⌎", + "Uring": "Ů", + "uring": "ů", + "urtri": "◹", + "Uscr": "𝒰", + "uscr": "𝓊", + "utdot": "⋰", + "Utilde": "Ũ", + "utilde": "ũ", + "utri": "▵", + "utrif": "▴", + "uuarr": "⇈", + "Uuml": "Ü", + "uuml": "ü", + "uwangle": "⦧", + "vangrt": "⦜", + "varepsilon": "ϵ", + "varkappa": "ϰ", + "varnothing": "∅", + "varphi": "ϕ", + "varpi": "ϖ", + "varpropto": "∝", + "varr": "↕", + "vArr": "⇕", + "varrho": "ϱ", + "varsigma": "ς", + "varsubsetneq": "⊊︀", + "varsubsetneqq": "⫋︀", + "varsupsetneq": "⊋︀", + "varsupsetneqq": "⫌︀", + "vartheta": "ϑ", + "vartriangleleft": "⊲", + "vartriangleright": "⊳", + "vBar": "⫨", + "Vbar": "⫫", + "vBarv": "⫩", + "Vcy": "В", + "vcy": "в", + "vdash": "⊢", + "vDash": "⊨", + "Vdash": "⊩", + "VDash": "⊫", + "Vdashl": "⫦", + "veebar": "⊻", + "vee": "∨", + "Vee": "⋁", + "veeeq": "≚", + "vellip": "⋮", + "verbar": "|", + "Verbar": "‖", + "vert": "|", + "Vert": "‖", + "VerticalBar": "∣", + "VerticalLine": "|", + "VerticalSeparator": "❘", + "VerticalTilde": "≀", + "VeryThinSpace": " ", + "Vfr": "𝔙", + "vfr": "𝔳", + "vltri": "⊲", + "vnsub": "⊂⃒", + "vnsup": "⊃⃒", + "Vopf": "𝕍", + "vopf": "𝕧", + "vprop": "∝", + "vrtri": "⊳", + "Vscr": "𝒱", + "vscr": "𝓋", + "vsubnE": "⫋︀", + "vsubne": "⊊︀", + "vsupnE": "⫌︀", + "vsupne": "⊋︀", + "Vvdash": "⊪", + "vzigzag": "⦚", + "Wcirc": "Ŵ", + "wcirc": "ŵ", + "wedbar": "⩟", + "wedge": "∧", + "Wedge": "⋀", + "wedgeq": "≙", + "weierp": "℘", + "Wfr": "𝔚", + "wfr": "𝔴", + "Wopf": "𝕎", + "wopf": "𝕨", + "wp": "℘", + "wr": "≀", + "wreath": "≀", + "Wscr": "𝒲", + "wscr": "𝓌", + "xcap": "⋂", + "xcirc": "◯", + "xcup": "⋃", + "xdtri": "▽", + "Xfr": "𝔛", + "xfr": "𝔵", + "xharr": "⟷", + "xhArr": "⟺", + "Xi": "Ξ", + "xi": "ξ", + "xlarr": "⟵", + "xlArr": "⟸", + "xmap": "⟼", + "xnis": "⋻", + "xodot": "⨀", + "Xopf": "𝕏", + "xopf": "𝕩", + "xoplus": "⨁", + "xotime": "⨂", + "xrarr": "⟶", + "xrArr": "⟹", + "Xscr": "𝒳", + "xscr": "𝓍", + "xsqcup": "⨆", + "xuplus": "⨄", + "xutri": "△", + "xvee": "⋁", + "xwedge": "⋀", + "Yacute": "Ý", + "yacute": "ý", + "YAcy": "Я", + "yacy": "я", + "Ycirc": "Ŷ", + "ycirc": "ŷ", + "Ycy": "Ы", + "ycy": "ы", + "yen": "¥", + "Yfr": "𝔜", + "yfr": "𝔶", + "YIcy": "Ї", + "yicy": "ї", + "Yopf": "𝕐", + "yopf": "𝕪", + "Yscr": "𝒴", + "yscr": "𝓎", + "YUcy": "Ю", + "yucy": "ю", + "yuml": "ÿ", + "Yuml": "Ÿ", + "Zacute": "Ź", + "zacute": "ź", + "Zcaron": "Ž", + "zcaron": "ž", + "Zcy": "З", + "zcy": "з", + "Zdot": "Ż", + "zdot": "ż", + "zeetrf": "ℨ", + "ZeroWidthSpace": "​", + "Zeta": "Ζ", + "zeta": "ζ", + "zfr": "𝔷", + "Zfr": "ℨ", + "ZHcy": "Ж", + "zhcy": "ж", + "zigrarr": "⇝", + "zopf": "𝕫", + "Zopf": "ℤ", + "Zscr": "𝒵", + "zscr": "𝓏", + "zwj": "‍", + "zwnj": "‌" + }; + +/***/ }, +/* 33 */ +/***/ function(module, exports) { + + module.exports = { + "Aacute": "Á", + "aacute": "á", + "Acirc": "Â", + "acirc": "â", + "acute": "´", + "AElig": "Æ", + "aelig": "æ", + "Agrave": "À", + "agrave": "à", + "amp": "&", + "AMP": "&", + "Aring": "Å", + "aring": "å", + "Atilde": "Ã", + "atilde": "ã", + "Auml": "Ä", + "auml": "ä", + "brvbar": "¦", + "Ccedil": "Ç", + "ccedil": "ç", + "cedil": "¸", + "cent": "¢", + "copy": "©", + "COPY": "©", + "curren": "¤", + "deg": "°", + "divide": "÷", + "Eacute": "É", + "eacute": "é", + "Ecirc": "Ê", + "ecirc": "ê", + "Egrave": "È", + "egrave": "è", + "ETH": "Ð", + "eth": "ð", + "Euml": "Ë", + "euml": "ë", + "frac12": "½", + "frac14": "¼", + "frac34": "¾", + "gt": ">", + "GT": ">", + "Iacute": "Í", + "iacute": "í", + "Icirc": "Î", + "icirc": "î", + "iexcl": "¡", + "Igrave": "Ì", + "igrave": "ì", + "iquest": "¿", + "Iuml": "Ï", + "iuml": "ï", + "laquo": "«", + "lt": "<", + "LT": "<", + "macr": "¯", + "micro": "µ", + "middot": "·", + "nbsp": " ", + "not": "¬", + "Ntilde": "Ñ", + "ntilde": "ñ", + "Oacute": "Ó", + "oacute": "ó", + "Ocirc": "Ô", + "ocirc": "ô", + "Ograve": "Ò", + "ograve": "ò", + "ordf": "ª", + "ordm": "º", + "Oslash": "Ø", + "oslash": "ø", + "Otilde": "Õ", + "otilde": "õ", + "Ouml": "Ö", + "ouml": "ö", + "para": "¶", + "plusmn": "±", + "pound": "£", + "quot": "\"", + "QUOT": "\"", + "raquo": "»", + "reg": "®", + "REG": "®", + "sect": "§", + "shy": "­", + "sup1": "¹", + "sup2": "²", + "sup3": "³", + "szlig": "ß", + "THORN": "Þ", + "thorn": "þ", + "times": "×", + "Uacute": "Ú", + "uacute": "ú", + "Ucirc": "Û", + "ucirc": "û", + "Ugrave": "Ù", + "ugrave": "ù", + "uml": "¨", + "Uuml": "Ü", + "uuml": "ü", + "Yacute": "Ý", + "yacute": "ý", + "yen": "¥", + "yuml": "ÿ" + }; + +/***/ }, +/* 34 */ +/***/ function(module, exports) { + + module.exports = { + "amp": "&", + "apos": "'", + "gt": ">", + "lt": "<", + "quot": "\"" + }; + +/***/ }, +/* 35 */ +/***/ function(module, exports, __webpack_require__) { + + var ElementType = __webpack_require__(36); + + var re_whitespace = /\s+/g; + var NodePrototype = __webpack_require__(37); + var ElementPrototype = __webpack_require__(38); + + function DomHandler(callback, options, elementCB){ + if(typeof callback === "object"){ + elementCB = options; + options = callback; + callback = null; + } else if(typeof options === "function"){ + elementCB = options; + options = defaultOpts; + } + this._callback = callback; + this._options = options || defaultOpts; + this._elementCB = elementCB; + this.dom = []; + this._done = false; + this._tagStack = []; + this._parser = this._parser || null; + } + + //default options + var defaultOpts = { + normalizeWhitespace: false, //Replace all whitespace with single spaces + withStartIndices: false, //Add startIndex properties to nodes + }; + + DomHandler.prototype.onparserinit = function(parser){ + this._parser = parser; + }; + + //Resets the handler back to starting state + DomHandler.prototype.onreset = function(){ + DomHandler.call(this, this._callback, this._options, this._elementCB); + }; + + //Signals the handler that parsing is done + DomHandler.prototype.onend = function(){ + if(this._done) return; + this._done = true; + this._parser = null; + this._handleCallback(null); + }; + + DomHandler.prototype._handleCallback = + DomHandler.prototype.onerror = function(error){ + if(typeof this._callback === "function"){ + this._callback(error, this.dom); + } else { + if(error) throw error; + } + }; + + DomHandler.prototype.onclosetag = function(){ + //if(this._tagStack.pop().name !== name) this._handleCallback(Error("Tagname didn't match!")); + var elem = this._tagStack.pop(); + if(this._elementCB) this._elementCB(elem); + }; + + DomHandler.prototype._addDomElement = function(element){ + var parent = this._tagStack[this._tagStack.length - 1]; + var siblings = parent ? parent.children : this.dom; + var previousSibling = siblings[siblings.length - 1]; + + element.next = null; + + if(this._options.withStartIndices){ + element.startIndex = this._parser.startIndex; + } + + if (this._options.withDomLvl1) { + element.__proto__ = element.type === "tag" ? ElementPrototype : NodePrototype; + } + + if(previousSibling){ + element.prev = previousSibling; + previousSibling.next = element; + } else { + element.prev = null; + } + + siblings.push(element); + element.parent = parent || null; + }; + + DomHandler.prototype.onopentag = function(name, attribs){ + var element = { + type: name === "script" ? ElementType.Script : name === "style" ? ElementType.Style : ElementType.Tag, + name: name, + attribs: attribs, + children: [] + }; + + this._addDomElement(element); + + this._tagStack.push(element); + }; + + DomHandler.prototype.ontext = function(data){ + //the ignoreWhitespace is officially dropped, but for now, + //it's an alias for normalizeWhitespace + var normalize = this._options.normalizeWhitespace || this._options.ignoreWhitespace; + + var lastTag; + + if(!this._tagStack.length && this.dom.length && (lastTag = this.dom[this.dom.length-1]).type === ElementType.Text){ + if(normalize){ + lastTag.data = (lastTag.data + data).replace(re_whitespace, " "); + } else { + lastTag.data += data; + } + } else { + if( + this._tagStack.length && + (lastTag = this._tagStack[this._tagStack.length - 1]) && + (lastTag = lastTag.children[lastTag.children.length - 1]) && + lastTag.type === ElementType.Text + ){ + if(normalize){ + lastTag.data = (lastTag.data + data).replace(re_whitespace, " "); + } else { + lastTag.data += data; + } + } else { + if(normalize){ + data = data.replace(re_whitespace, " "); + } + + this._addDomElement({ + data: data, + type: ElementType.Text + }); + } + } + }; + + DomHandler.prototype.oncomment = function(data){ + var lastTag = this._tagStack[this._tagStack.length - 1]; + + if(lastTag && lastTag.type === ElementType.Comment){ + lastTag.data += data; + return; + } + + var element = { + data: data, + type: ElementType.Comment + }; + + this._addDomElement(element); + this._tagStack.push(element); + }; + + DomHandler.prototype.oncdatastart = function(){ + var element = { + children: [{ + data: "", + type: ElementType.Text + }], + type: ElementType.CDATA + }; + + this._addDomElement(element); + this._tagStack.push(element); + }; + + DomHandler.prototype.oncommentend = DomHandler.prototype.oncdataend = function(){ + this._tagStack.pop(); + }; + + DomHandler.prototype.onprocessinginstruction = function(name, data){ + this._addDomElement({ + name: name, + data: data, + type: ElementType.Directive + }); + }; + + module.exports = DomHandler; + + +/***/ }, +/* 36 */ +/***/ function(module, exports) { + + //Types of elements found in the DOM + module.exports = { + Text: "text", //Text + Directive: "directive", // + Comment: "comment", // + Script: "script", //