// Copyright (c) 2014,2015 Walter Bender
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// You should have received a copy of the GNU General Public License
// along with this library; if not, write to the Free Software
// Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA

// There are three "classes" defined in this file: ProtoBlocks,
// Blocks, and Block. Protoblocks are the prototypes from which Blocks
// are created; Blocks is the list of all blocks; and Block is a block
// instance.

// Protoblock contain generic information about blocks and some
// methods common to all blocks.
function ProtoBlock(name) {
    // Name is used run-dictionary index, and palette label.
    this.name = name;
    // The palette to which this block is assigned.
    this.palette = null;
    // The graphic style used by the block.
    this.style = null;
    // The generator function used to create the artwork
    this.generator = null;
    // Does the block expand (or collapse) when other blocks are
    // attached? e.g., start, repeat...
    this.expandable = false;
    // Is this block a parameter? Parameters have their labels
    // overwritten with their current value.
    this.parameter = false;
    // How many "non-flow" arguments does a block have? (flow is
    // vertical down a stack; args are horizontal. The pendown block
    // has 0 args; the forward block has 1 arg; the setxy block has 2
    // args.
    this.args = 0;
    // Default values for block parameters, e.g., forward 100 or right 90.
    this.defaults = [];
    // What is the size of the block prior to any expansion?
    this.size = 1.0;
    // Dock types are a list of the types associated with the docking points.
    this.dockTypes = [];
    // Static labels are generated as part of the inline SVG.
    this.staticLabels = [];
    // Default fontsize used for static labels.
    this.fontsize = null;
    // Extra block width for long labels
    this.extraWidth = 0;
    // Block scale
    this.scale = DEFAULTBLOCKSCALE;
    // The filepath of the image.
    this.image = null;
    // Hidden: don't show on any palette
    this.hidden = false;
    // Disabled: use inactive colors
    this.disabled = false;

    this.adjustWidthToLabel = function() {
        if (this.staticLabels.length == 0) {
            return;
        }
        var labelLength = this.staticLabels[0].length;
        if ((labelLength - 6) > 0) {
            // TODO: better estimation of label size
            this.extraWidth += (labelLength - 6) * 4;
        }
    }

    // What follows are the initializations for different block
    // styles.

    // E.g., penup, pendown
    this.zeroArgBlock = function() {
        this.args = 0;
        this.dockTypes.push('out');
        this.dockTypes.push('in');
        this.generator = this.zeroArgBlockGenerator;
    }

    this.zeroArgBlockGenerator = function() {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        svg.setTab(true);
        svg.setSlot(true);
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        svg.setExpand(30 + this.extraWidth, 0, 0, 0);
        var artwork = svg.basicBlock();
	return [artwork, svg.docks];
    }

    // E.g., break
    this.basicBlockNoFlow = function() {
        this.args = 0;
        this.dockTypes.push('out');
        this.dockTypes.push('unavailable');
        this.generator = this.basicBlockNoFlowGenerator;
    }

    this.basicBlockNoFlowGenerator = function() {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        svg.setSlot(true);
        svg.setTail(true);
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        svg.setExpand(30 + this.extraWidth, 0, 0, 0);
        var artwork = svg.basicBlock();
	return [artwork, svg.docks];
    }

    // E.g., collapsed
    this.basicBlockCollapsed = function() {
        this.args = 0;
        this.dockTypes.push('unavailable');
        this.dockTypes.push('unavailable');
        this.generator = this.basicBlockCollapsedGenerator;
    }

    this.basicBlockCollapsedGenerator = function() {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        svg.setCap(true);
        svg.setTail(true);
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        svg.setExpand(30 + this.extraWidth, 0, 0, 0);
        var artwork = svg.basicBlock();
	return [artwork, svg.docks];
    }

    // E.g., forward, right
    this.oneArgBlock = function() {
        this.args = 1;
        this.dockTypes.push('out');
        this.dockTypes.push('numberin');
        this.dockTypes.push('in');
        this.generator = this.oneArgBlockGenerator;
    }

    this.oneArgBlockGenerator = function() {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        svg.setTab(true);
        svg.setInnies([true]);
        svg.setSlot(true);
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        svg.setExpand(30 + this.extraWidth, 0, 0, 0);
        var artwork = svg.basicBlock();
	return [artwork, svg.docks];
    }

    // E.g., wait for
    this.oneBooleanArgBlock = function() {
        this.args = 1;
        this.size = 3;
        this.dockTypes.push('in');
        this.dockTypes.push('booleanin');
        this.dockTypes.push('out');
        this.generator = this.oneBooleanArgBlockGenerator;
    }

    this.oneBooleanArgBlockGenerator = function() {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        svg.setTab(true);
        svg.setSlot(true);
        svg.setBoolean(true);
        svg.setClampCount(0);
        svg.setExpand(0, 0, 0, 0);
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        svg.setExpand(30 + this.extraWidth, 0, 0, 0);
        var artwork = svg.basicClamp();
	return [artwork, svg.docks];
    }

    // E.g., setxy. These are expandable.
    this.twoArgBlock = function() {
        this.expandable = true;
        this.style = 'twoarg';
        this.size = 2;
        this.args = 2;
        this.dockTypes.push('out');
        this.dockTypes.push('numberin');
        this.dockTypes.push('numberin');
        this.dockTypes.push('in');
        this.generator = this.twoArgBlockGenerator;
    }

    this.twoArgBlockGenerator = function(expandY) {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        svg.setTab(true);
        svg.setInnies([true, true]);
        svg.setSlot(true);
        if (expandY) {
            svg.setExpand(30 + this.extraWidth, (expandY - 1) * STANDARDBLOCKHEIGHT / 2, 0, 0);
        } else {
            svg.setExpand(30 + this.extraWidth, 0, 0, 0);
        }
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        var artwork = svg.basicBlock();
	return [artwork, svg.docks];
    }

    // E.g., ??? These are expandable.
    this.threeArgBlock = function() {
        this.expandable = true;
        this.style = 'twoarg';
        this.size = 3;
        this.args = 3;
        this.dockTypes.push('out');
        this.dockTypes.push('numberin');
        this.dockTypes.push('numberin');
        this.dockTypes.push('numberin');
        this.dockTypes.push('in');
        this.generator = this.threeArgBlockGenerator;
    }

    this.threeArgBlockGenerator = function(expandY) {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        svg.setTab(true);
        svg.setInnies([true, true, true]);
        svg.setSlot(true);
        if (expandY) {
            svg.setExpand(30 + this.extraWidth, (expandY - 1) * STANDARDBLOCKHEIGHT / 2, 0, 0);
        } else {
            svg.setExpand(30 + this.extraWidth, 0, 0, 0);
        }
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        var artwork = svg.basicBlock();
	return [artwork, svg.docks];
    }

    // E.g., sqrt, box
    this.oneArgMathBlock = function() {
        this.style = 'arg';
        this.size = 1;
        this.args = 1;
        this.parameter = true;
        this.dockTypes.push('numberout');
        this.dockTypes.push('numberin');
        this.generator = this.oneArgMathBlockGenerator;
    }

    this.oneArgMathBlockGenerator = function() {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        svg.setSlot(false);
        svg.setInnies([true]);
        svg.setOutie(true);
        svg.setTab(false);
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        svg.setExpand(30 + this.extraWidth, 0, 0, 0);
        var artwork = svg.basicBlock();
	return [artwork, svg.docks];
    }

    // E.g., plus, minus, multiply, divide. These are also expandable.
    this.twoArgMathBlock = function() {
        this.expandable = true;
        this.style = 'arg';
        this.size = 2;
        this.args = 2;
        this.parameter = true;
        this.dockTypes.push('numberout');
        this.dockTypes.push('numberin');
        this.dockTypes.push('numberin');
        this.generator = this.twoArgMathBlockGenerator;
    }

    this.twoArgMathBlockGenerator = function(expandY) {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        svg.setSlot(false);
        svg.setInnies([true, true]);
        svg.setOutie(true);
        svg.setTab(false);
        if (expandY) {
            svg.setExpand(30 + this.extraWidth, (expandY - 1) * STANDARDBLOCKHEIGHT / 2, 0, 0);
        } else {
            svg.setExpand(30 + this.extraWidth, 0, 0, 0);
        }
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        var artwork = svg.basicBlock();
	return [artwork, svg.docks];
    }

    // 
    this.threeArgMathBlock = function() {
        this.expandable = true;
        this.style = 'arg';
        this.size = 3;
        this.args = 3;
        this.parameter = true;
        this.dockTypes.push('numberout');
        this.dockTypes.push('numberin');
        this.dockTypes.push('numberin');
        this.dockTypes.push('numberin');
        this.generator = this.threeArgMathBlockGenerator;
    }

    this.threeArgMathBlockGenerator = function(expandY) {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        svg.setSlot(false);
        svg.setInnies([true, true, true]);
        svg.setOutie(true);
        svg.setTab(false);
        if (expandY) {
            svg.setExpand(30 + this.extraWidth, (expandY - 1) * STANDARDBLOCKHEIGHT / 2, 0, 0);
        } else {
            svg.setExpand(30 + this.extraWidth, 0, 0, 0);
        }
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        var artwork = svg.basicBlock();
	return [artwork, svg.docks];
    }

    // E.g., number, string. Value blocks get DOM textareas associated
    // with them so their values can be edited by the user.
    this.valueBlock = function() {
        this.style = 'value';
        this.size = 1;
        this.args = 0;
        this.dockTypes.push('numberout');
        this.generator = this.valueBlockGenerator;
    }

    this.valueBlockGenerator = function() {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        // Extra room for parameter label
        svg.setExpand(60 + this.extraWidth, 0, 0, 0);
        svg.setOutie(true);
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        var artwork = svg.basicBox();
	return [artwork, svg.docks];
    }

    // E.g., media. Media blocks invoke a chooser and a thumbnail
    // image is overlayed to represent the data associated with the
    // block.
    this.mediaBlock = function() {
        this.style = 'value';
        this.size = 2;
        this.args = 0;
        this.dockTypes.push('mediaout');
        this.generator = this.mediaBlockGenerator;
    }

    this.mediaBlockGenerator = function() {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        // Extra room for graphics
        svg.setExpand(60 + this.extraWidth, 23, 0, 0);
        svg.setOutie(true);
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        var artwork = svg.basicBox();
	return [artwork, svg.docks];
    }

    // E.g., start. A "child" flow is docked in an expandable clamp.
    // There are no additional arguments and no flow above or below.
    this.stackClampZeroArgBlock = function() {
        this.style = 'clamp';
        this.expandable = true;
        this.size = 3;
        this.args = 1;
        this.dockTypes.push('unavailable');
        this.dockTypes.push('in');
        this.dockTypes.push('unavailable');
        this.generator = this.stackClampZeroArgBlockGenerator;
    }

    this.stackClampZeroArgBlockGenerator = function(slots) {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        svg.setCap(true);
        svg.setTail(true);
        svg.setExpand(20 + this.extraWidth, 0, 0, 0);
        if (slots) {
            svg.setClampSlots(0, slots);
        } else {
            svg.setClampSlots(0, 1);
        }
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        var artwork = svg.basicClamp();
	return [artwork, svg.docks];
    }

    // E.g., emptyclamp. Unlike start, there is a flow above and below.
    this.flowClampBlock = function() {
        this.style = 'clamp';
        this.expandable = true;
        this.size = 2;
        this.args = 1;
        this.dockTypes.push('out');
        this.dockTypes.push('in');
        this.dockTypes.push('in');
        this.generator = this.flowClampBlockGenerator;
    }

    this.flowClampBlockGenerator = function(slots) {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        svg.setTab(true);

        svg.setSlot(true);
        svg.setExpand(20 + this.extraWidth, 0, 0, 0);
        if (slots) {
            svg.setClampSlots(0, slots);
        } else {
            svg.setClampSlots(0, 1);
        }
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        var artwork = svg.basicClamp();
	return [artwork, svg.docks];
    }

    // E.g., repeat. Unlike action, there is a flow above and below.
    this.flowClampOneArgBlock = function() {
        this.style = 'clamp';
        this.expandable = true;
        this.size = 2;
        this.args = 2;
        this.dockTypes.push('out');
        this.dockTypes.push('numberin');
        this.dockTypes.push('in');
        this.dockTypes.push('in');
        this.generator = this.flowClampOneArgBlockGenerator;
    }

    this.flowClampOneArgBlockGenerator = function(slots) {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        svg.setTab(true);
        svg.setSlot(true);
        svg.setInnies([true]);
        svg.setExpand(20 + this.extraWidth, 0, 0, 0);
        if (slots) {
            svg.setClampSlots(0, slots);
        } else {
            svg.setClampSlots(0, 1);
        }
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        var artwork = svg.basicClamp();
	return [artwork, svg.docks];
    }

    // E.g., do with args: innies instead of interior slots.
    this.argClampOneArgBlock = function() {
        this.style = 'argclamp';
        this.expandable = true;
        this.size = 3;
        this.args = 2;
        this.dockTypes.push('out');
        this.dockTypes.push('textin');
        this.dockTypes.push('anyin');
        this.dockTypes.push('in');
        this.generator = this.argClampOneArgBlockGenerator;
    }

    this.argClampOneArgBlockGenerator = function(slots) {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        svg.setTab(true);
        svg.setSlot(true);
        svg.setInnies([true]);
        svg.setExpand(20 + this.extraWidth, 0, 0, 0);
        if (slots) {
            svg.setClampSlots(0, slots);
        } else {
            svg.setClampSlots(0, [1]);
        }
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        var artwork = svg.argClamp();
	return [artwork, svg.docks];
    }

    // E.g., calculate with args: innies instead of interior slots.
    this.argClampOneArgMathBlock = function() {
        this.style = 'argclamparg';
        this.expandable = true;
        this.size = 3;
        this.args = 2;
        this.dockTypes.push('anyout');
        this.dockTypes.push('textin');
        this.dockTypes.push('anyin');
        this.generator = this.argClampOneArgMathBlockGenerator;
    }

    this.argClampOneArgMathBlockGenerator = function(slots) {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        svg.setInnies([true]);
        svg.setOutie(true);
        svg.setTab(false);
        svg.setSlot(false);
        svg.setExpand(20 + this.extraWidth, 0, 0, 0);
        if (slots) {
            svg.setClampSlots(0, slots);
        } else {
            svg.setClampSlots(0, [1]);
        }
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        var artwork = svg.argClamp();
	return [artwork, svg.docks];
    }

    // E.g., named do with args: innies instead of interior slots.
    this.argClampBlock = function() {
        this.style = 'argclamp';
        this.expandable = true;
        this.size = 3;
        this.args = 2;
        this.dockTypes.push('out');
        this.dockTypes.push('anyin');
        this.dockTypes.push('in');
        this.generator = this.argClampBlockGenerator;
    }

    this.argClampBlockGenerator = function(slots) {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        svg.setTab(true);
        svg.setSlot(true);
        svg.setExpand(20 + this.extraWidth, 0, 0, 0);
        if (slots) {
            svg.setClampSlots(0, slots);
        } else {
            svg.setClampSlots(0, [1]);
        }
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        var artwork = svg.argClamp();
	return [artwork, svg.docks];
    }

    // E.g., named calculate with args: innies instead of interior slots.
    this.argClampMathBlock = function() {
        this.style = 'argclamparg';
        this.expandable = true;
        this.size = 3;
        this.args = 2;
        this.dockTypes.push('anyout');
        this.dockTypes.push('anyin');
        this.generator = this.argClampMathBlockGenerator;
    }

    this.argClampMathBlockGenerator = function(slots) {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        svg.setOutie(true);
        svg.setTab(false);
        svg.setSlot(false);
        svg.setExpand(20 + this.extraWidth, 0, 0, 0);
        if (slots) {
            svg.setClampSlots(0, slots);
        } else {
            svg.setClampSlots(0, [1]);
        }
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        var artwork = svg.argClamp();
	return [artwork, svg.docks];
    }

    // E.g., if.  A "child" flow is docked in an expandable clamp. The
    // additional argument is a boolean. There is flow above and below.
    this.flowClampBooleanArgBlock = function() {
        this.style = 'clamp';
        this.expandable = true;
        this.size = 3;
        this.args = 2;
        this.dockTypes.push('out');
        this.dockTypes.push('booleanin');
        this.dockTypes.push('in');
        this.dockTypes.push('in');
        this.generator = this.flowClampBooleanArgBlockGenerator;
    }

    this.flowClampBooleanArgBlockGenerator = function(slots) {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        svg.setTab(true);
        svg.setBoolean(true);
        svg.setSlot(true);
        svg.setExpand(this.extraWidth, 0, 0, 0);
        if (slots) {
            svg.setClampSlots(0, slots);
        } else {
            svg.setClampSlots(0, 1);
        }
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        var artwork = svg.basicClamp();
	return [artwork, svg.docks];
    }

    // E.g., if then else.  Two "child" flows are docked in expandable
    // clamps. The additional argument is a boolean. There is flow
    // above and below.
    this.doubleFlowClampBooleanArgBlock = function() {
        this.style = 'doubleclamp';
        this.expandable = true;
        this.size = 4;
        this.args = 3;
        this.dockTypes.push('out');
        this.dockTypes.push('booleanin');
        this.dockTypes.push('in');
        this.dockTypes.push('in');
        this.dockTypes.push('in');
	this.generator = this.doubleFlowClampBooleanArgBlockGenerator;
    }

    this.doubleFlowClampBooleanArgBlockGenerator = function(bottomSlots, topSlots) {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        svg.setTab(true);
        svg.setSlot(true);
        svg.setBoolean(true);
        svg.setClampCount(this.scale);
        if (topSlots) {
            svg.setClampSlots(0, topSlots);
        } else {
            svg.setClampSlots(0, 1);
        }
        if (bottomSlots) {
            svg.setClampSlots(1, bottomSlots);
        } else {
            svg.setClampSlots(1, 1);
        }
        svg.setExpand(this.extraWidth, 0, 0, 0);
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        var artwork = svg.basicClamp();
	return [artwork, svg.docks];
    }

    // E.g., forever. Unlike start, there is flow above and below.
    this.flowClampZeroArgBlock = function() {
        this.style = 'clamp';
        this.expandable = true;
        this.size = 2;
        this.args = 1;
        this.dockTypes.push('out');
        this.dockTypes.push('in');
        this.dockTypes.push('in');
        this.generator = this.flowClampZeroArgBlockGenerator;
    }

    this.flowClampZeroArgBlockGenerator = function(slots) {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        svg.setTab(true);
        svg.setSlot(true);
        svg.setExpand(10 + this.extraWidth, 0, 0, 0);
        if (slots) {
            svg.setClampSlots(0, slots);
        } else {
            svg.setClampSlots(0, 1);
        }
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        var artwork = svg.basicClamp();
        return [artwork, svg.docks];
    }

    // E.g., action. A "child" flow is docked in an expandable clamp.
    // The additional argument is a name. Again, no flow above or below.
    this.stackClampOneArgBlock = function() {
        this.style = 'clamp';
        this.expandable = true;
        this.size = 3;
        this.args = 1;
        this.dockTypes.push('unavailable');
        this.dockTypes.push('anyin');
        this.dockTypes.push('in');
        this.dockTypes.push('unavailable');
        this.generator = this.stackClampOneArgBlockGenerator;
    }

    this.stackClampOneArgBlockGenerator = function(slots) {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        svg.setCap(true);
        svg.setTail(true);
        svg.setInnies([true]);
        svg.setExpand(10 + this.extraWidth, 0, 0, 0);
        if (slots) {
            svg.setClampSlots(0, slots);
        } else {
            svg.setClampSlots(0, 1);
        }
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        var artwork = svg.basicClamp();
        return [artwork, svg.docks];
    }

    // E.g., mouse button.
    this.booleanZeroArgBlock = function() {
        this.style = 'arg';
        this.size = 1;
        this.args = 0;
        this.dockTypes.push('booleanout');
        this.generator = this.booleanZeroArgBlockGenerator;
    }

    this.booleanZeroArgBlockGenerator = function() {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        svg.setExpand(60 + this.extraWidth, 0, 0, 4);
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        var artwork = svg.booleanNot(true);
        return [artwork, svg.docks];
    }

    // E.g., named sensor blocks
    this.booleanOneArgBlock = function() {
        this.style = 'arg';
        this.size = 2;
        this.args = 1;
        this.parameter = true;
        this.dockTypes.push('booleanout');
        this.dockTypes.push('textin');
        this.generator = this.booleanOneArgBlockGenerator;
    }

    this.booleanOneArgBlockGenerator = function() {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        svg.setExpand(20 + this.extraWidth, 0, 0, 0);
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        svg.setInnies([true]);
        var artwork = svg.booleanNot(true);  // OneArg
        return [artwork, svg.docks];
    }

    // E.g., not
    this.booleanOneBooleanArgBlock = function() {
        this.style = 'arg';
        this.size = 2;
        this.args = 1;
        this.parameter = true;
        this.dockTypes.push('booleanout');
        this.dockTypes.push('booleanin');
        this.generator = this.booleanOneBooleanArgBlockGenerator;
    }

    this.booleanOneBooleanArgBlockGenerator = function() {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        svg.setExpand(20 + this.extraWidth, 0, 0, 0);
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        var artwork = svg.booleanNot(false);
        return [artwork, svg.docks];
    }

    // E.g., and, or
    this.booleanTwoBooleanArgBlock = function() {
        this.style = 'arg';
        this.size = 3;
        this.args = 2;
        this.parameter = true;
        this.dockTypes.push('booleanout');
        this.dockTypes.push('booleanin');
        this.dockTypes.push('booleanin');
        this.generator = this.booleanTwoBooleanArgBlockGenerator;
    }

    this.booleanTwoBooleanArgBlockGenerator = function() {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        svg.setExpand(20 + this.extraWidth, 0, 0, 0);
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        var artwork = svg.booleanAndOr();
        return [artwork, svg.docks];
    }

    // E.g., greater, less, equal
    this.booleanTwoArgBlock = function() {
        this.style = 'arg';
        this.size = 2;
        this.args = 2;
        this.parameter = true;
        this.expandable = true;
        this.dockTypes.push('booleanout');
        this.dockTypes.push('numberin');
        this.dockTypes.push('numberin');
        this.generator = this.booleanTwoArgBlockGenerator;
    }

    this.booleanTwoArgBlockGenerator = function(expandY) {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        if (expandY) {
            svg.setExpand(10 + this.extraWidth, (expandY - 1) * STANDARDBLOCKHEIGHT / 2, 0, 0);
        } else {
            svg.setExpand(10 + this.extraWidth, 0, 0, 0);
        }
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        var artwork = svg.booleanCompare();
        return [artwork, svg.docks];
    }

    // E.g., color, shade, pensize, ...
    this.parameterBlock = function() {
        this.style = 'arg';
        this.parameter = true;
        this.size = 1;
        this.args = 0;
        this.dockTypes.push('numberout');
        this.generator = this.parameterBlockGenerator;
    }

    this.parameterBlockGenerator = function() {
        var svg = new SVG();
        svg.init();
        svg.setScale(this.scale);
        // Extra room for parameter label
        svg.setExpand(70 + this.extraWidth, 0, 0, 0);
        svg.setOutie(true);
        if (this.fontsize) {
            svg.setFontSize(this.fontsize);
        }
        var artwork = svg.basicBox();
        return [artwork, svg.docks];
    }
}
