text: /**
   * Copyright 2017 Juan Carlos Orozco Arena
   * Apache License Version 2.0
   */
  
  /**
   * @fileoverview
   * The BlockDefinitionExtractor is a class that generates a workspace DOM
   * suitable for the BlockFactory's block editor, derived from an example
   * Blockly.Block.
   *
   * <code>
   * var workspaceDom = new BlockDefinitionExtractor()
   *     .buildBlockFactoryWorkspace(exampleBlocklyBlock);
   * Blockly.Xml.domToWorkspace(workspaceDom, BlockFactory.mainWorkspace);
   * </code>
   *
   * The <code>exampleBlocklyBlock</code> is usually the block loaded into the
   * preview workspace after manually entering the block definition.
   *
   * @author JC-Orozco (Juan Carlos Orozco), AnmAtAnm (Andrew n marshall)
   */
  'use strict';
  
  /**
   * Namespace for BlockDefinitionExtractor.
   */
  goog.provide('BlockDefinitionExtractor');
  
  
  /**
   * Class to contain all functions needed to extract block definition from
   * the block preview data structure.
   * @namespace
   */
  BlockDefinitionExtractor = BlockDefinitionExtractor || Object.create(null);
  
  /**
   * Builds a BlockFactory workspace that reflects the block structure of the
   * exmaple block.
   *
   * @param {!Blockly.Block} block The reference block from which the definition
   *     will be extracted.
   * @return {!Element} Returns the root workspace DOM <xml> for the block editor
   *     workspace.
   */
  BlockDefinitionExtractor.buildBlockFactoryWorkspace = function(block) {
    var workspaceXml = goog.dom.createDom('xml');
    workspaceXml.append(
        BlockDefinitionExtractor.factoryBase_(block, block.type));
  
    return workspaceXml;
  };
  
  /**
   * Helper function to create a new Element with the provided attributes and
   * inner text.
   *
   * @param {string} name New element tag name.
   * @param {Map<String,String>} opt_attrs Optional list of attributes.
   * @param {string?} opt_text Optional inner text.
   * @return {!Element} The newly created element.
   * @private
   */
  BlockDefinitionExtractor.newDomElement_ = function(name, opt_attrs, opt_text) {
    // Avoid createDom(..)'s attributes argument for being too HTML specific.
    var elem = goog.dom.createDom(name);
    if (opt_attrs) {
      for (var key in opt_attrs) {
        elem.setAttribute(key, opt_attrs[key]);
      }
    }
    if (opt_text) {
      elem.append(opt_text);
    }
    return elem;
  };
  
  /**
   * Creates an connection type constraint <block> Element representing the
   * requested type.
   *
   * @param {string} type Type name of desired connection constraint.
   * @return {!Element} The <block> representing the the constraint type.
   * @private
   */
  BlockDefinitionExtractor.buildBlockForType_ = function(type) {
    switch (type) {
      case 'Null':
        return BlockDefinitionExtractor.typeNull_();
      case 'Boolean':
        return BlockDefinitionExtractor.typeBoolean_();
      case 'Number':
        return BlockDefinitionExtractor.typeNumber_();
      case 'String':
        return BlockDefinitionExtractor.typeString_();
      case 'Array':
        return BlockDefinitionExtractor.typeList_();
      default:
        return BlockDefinitionExtractor.typeOther_(type);
    }
  };
  
  /**
   * Constructs a <block> element representing the type constraints of the
   * provided connection.
   *
   * @param {!Blockly.Connection} connection The connection with desired
   *     connection constraints.
   * @return {!Element} The root <block> element of the constraint definition.
   * @private
   */
  BlockDefinitionExtractor.buildTypeConstraintBlockForConnection_ =
      function(connection)
  {
    var typeBlock;
    if (connection.check_) {
      if (connection.check_.length < 1) {
        typeBlock = BlockDefinitionExtractor.typeNullShadow_();
      } else if (connection.check_.length === 1) {
        typeBlock = BlockDefinitionExtractor.buildBlockForType_(
            connection.check_[0]);
      } else if (connection.check_.length > 1 ) {
        typeBlock = BlockDefinitionExtractor.typeGroup_(connection.check_);
      }
    } else {
      typeBlock = BlockDefinitionExtractor.typeNullShadow_();
    }
    return typeBlock;
  };
  
  /**
   * Creates the root "factory_base" <block> element for the block definition.
   *
   * @param {!Blockly.Block} block The example block from which to extract the
   *     definition.
   * @param {string} name Block name.
   * @return {!Element} The factory_base block element.
   * @private
   */
  BlockDefinitionExtractor.factoryBase_ = function(block, name) {
    BlockDefinitionExtractor.src = {root: block, current: block};
    var factoryBaseEl =
        BlockDefinitionExtractor.newDomElement_('block', {type: 'factory_base'});
    factoryBaseEl.append(BlockDefinitionExtractor.newDomElement_(
        'field', {name: 'NAME'}, name));
    factoryBaseEl.append(BlockDefinitionExtractor.buildInlineField_(block));
  
    BlockDefinitionExtractor.buildConnections_(block, factoryBaseEl);
  
    var inputsStatement = BlockDefinitionExtractor.newDomElement_(
        'statement', {name: 'INPUTS'});
    inputsStatement.append(BlockDefinitionExtractor.parseInputs_(block));
    factoryBaseEl.append(inputsStatement);
  
    var tooltipValue =
        BlockDefinitionExtractor.newDomElement_('value', {name: 'TOOLTIP'});
    tooltipValue.append(BlockDefinitionExtractor.text_(block.tooltip));
    factoryBaseEl.append(tooltipValue);
  
    var helpUrlValue =
        BlockDefinitionExtractor.newDomElement_('value', {name: 'HELPURL'});
    helpUrlValue.append(BlockDefinitionExtractor.text_(block.helpUrl));
    factoryBaseEl.append(helpUrlValue);
  
    // Convert colour_ to hue value 0-360 degrees
    // TODO(#1247): Solve off-by-one errors.
    // TODO: Deal with colors that don't map to standard hues. (Needs improved
    //     block definitions.)
    var colour_hue = Math.floor(
        goog.color.hexToHsv(block.colour_)[0]);  // Off by one... sometimes
    var colourBlock = BlockDefinitionExtractor.colourBlockFromHue_(colour_hue);
    var colourInputValue =
        BlockDefinitionExtractor.newDomElement_('value', {name: 'COLOUR'});
    colourInputValue.append(colourBlock);
    factoryBaseEl.append(colourInputValue);
    return factoryBaseEl;
  };
  
  /**
   * Generates the appropriate <field> element for the block definition's
   * CONNECTIONS field, which determines the next, previous, and output
   * connections.
   *
   * @param {!Blockly.Block} block The example block from which to extract the
   *     definition.
   * @param {!Element} factoryBaseEl The root of the block definition.
   * @private
   */
  BlockDefinitionExtractor.buildConnections_ = function(block, factoryBaseEl) {
    var connections = 'NONE';
    if (block.outputConnection) {
      connections = 'LEFT';
    } else {
      if (block.previousConnection) {
        if (block.nextConnection) {
          connections = 'BOTH';
        } else {
          connections = 'TOP';
        }
      } else if (block.nextConnection) {
        connections = 'BOTTOM';
      }
    }
    factoryBaseEl.append(BlockDefinitionExtractor.newDomElement_(
        'field', {name: 'CONNECTIONS'}, connections));
  
    if (connections === 'LEFT') {
      var inputValue =
          BlockDefinitionExtractor.newDomElement_('value', {name: 'OUTPUTTYPE'});
      inputValue.append(
          BlockDefinitionExtractor.buildTypeConstraintBlockForConnection_(
              block.outputConnection));
      factoryBaseEl.append(inputValue);
    } else {
      if (connections === 'UP' || connections === 'BOTH') {
        var inputValue =
            BlockDefinitionExtractor.newDomElement_('value', {name: 'TOPTYPE'});
        inputValue.append(
            BlockDefinitionExtractor.buildTypeConstraintBlockForConnection_(
                block.previousConnection));
        factoryBaseEl.append(inputValue);
      }
      if (connections === 'DOWN' || connections === 'BOTH') {
        var inputValue = BlockDefinitionExtractor.newDomElement_(
            'value', {name: 'BOTTOMTYPE'});
        inputValue.append(
            BlockDefinitionExtractor.buildTypeConstraintBlockForConnection_(
                block.nextConnection));
        factoryBaseEl.append(inputValue);
      }
    }
  };
  
  /**
   * Generates the appropriate <field> element for the block definition's INLINE
   * field.
   *
   * @param {!Blockly.Block} block The example block from which to extract the
   *     definition.
   * @return {Element} The INLINE <field> with value 'AUTO', 'INT' (internal) or
   *     'EXT' (external).
   * @private
   */
  BlockDefinitionExtractor.buildInlineField_ = function(block) {
    var inline = 'AUTO'; // When block.inputsInlineDefault === undefined
    if (block.inputsInlineDefault === true) {
      inline = 'INT';
    } else if (block.inputsInlineDefault === false) {
      inline = 'EXT';
    }
    return BlockDefinitionExtractor.newDomElement_(
        'field', {name: 'INLINE'}, inline);
  };
  
  /**
   * Constructs a sequence of <block> elements that represent the inputs of the
   * provided block.
   *
   * @param {!Blockly.Block} block The source block to copy the inputs of.
   * @return {Element} The fist <block> element of the sequence
   *     (and the root of the constructed DOM).
   * @private
   */
  BlockDefinitionExtractor.parseInputs_ = function(block) {
    var firstInputDefElement = null;
    var lastInputDefElement = null;
    for (var i = 0; i < block.inputList.length; i++) {
      var input = block.inputList[i];
      var align = 'LEFT'; // Left alignment is the default.
      if (input.align === Blockly.ALIGN_CENTRE) {
        align = 'CENTRE';
      } else if (input.align === Blockly.ALIGN_RIGHT) {
        align = 'RIGHT';
      }
  
      var inputDefElement = BlockDefinitionExtractor.input_(input, align);
      if (lastInputDefElement) {
        var next = BlockDefinitionExtractor.newDomElement_('next');
        next.append(inputDefElement);
        lastInputDefElement.append(next);
      } else {
        firstInputDefElement = inputDefElement;
      }
      lastInputDefElement = inputDefElement;
    }
    return firstInputDefElement;
  };
  
  /**
   * Creates a <block> element representing a block input.
   *
   * @param {!Blockly.Input} input The input object.
   * @param {string} align Can be left, right or centre.
   * @return {!Element} The <block> element that defines the input.
   * @private
   */
  BlockDefinitionExtractor.input_ = function(input, align) {
    var isDummy = (input.type === Blockly.DUMMY_INPUT);
    var inputTypeAttr =
        isDummy ? 'input_dummy' :
        (input.type === Blockly.INPUT_VALUE) ? 'input_value' : 'input_statement';
    var inputDefBlock =
        BlockDefinitionExtractor.newDomElement_('block', {type: inputTypeAttr});
  
    if (!isDummy) {
      inputDefBlock.append(BlockDefinitionExtractor.newDomElement_(
          'field', {name: 'INPUTNAME'}, input.name));
    }
    inputDefBlock.append(BlockDefinitionExtractor.newDomElement_(
        'field', {name: 'ALIGN'}, align));
  
    var fieldsDef = BlockDefinitionExtractor.newDomElement_(
        'statement', {name: 'FIELDS'});
    var fieldsXml = BlockDefinitionExtractor.buildFields_(input.fieldRow);
    fieldsDef.append(fieldsXml);
    inputDefBlock.append(fieldsDef);
  
    if (!isDummy) {
      var typeValue = BlockDefinitionExtractor.newDomElement_(
          'value', {name: 'TYPE'});
      typeValue.append(
          BlockDefinitionExtractor.buildTypeConstraintBlockForConnection_(
              input.connection));
      inputDefBlock.append(typeValue);
    }
  
    return inputDefBlock;
  };
  
  /**
   * Constructs a sequence <block> elements representing the field definition.
   * @param {Array<Blockly.Field>} fieldRow A list of fields in a Blockly.Input.
   * @return {Element} The fist <block> element of the sequence
   *     (and the root of the constructed DOM).
   * @private
   */
  BlockDefinitionExtractor.buildFields_ = function(fieldRow) {
    var firstFieldDefElement = null;
    var lastFieldDefElement = null;
  
    for (var i = 0; i < fieldRow.length; i++) {
      var field = fieldRow[i];
      var fieldDefElement = BlockDefinitionExtractor.buildFieldElement_(field);
  
      if (lastFieldDefElement) {
        var next = BlockDefinitionExtractor.newDomElement_('next');
        next.append(fieldDefElement);
        lastFieldDefElement.append(next);
      } else {
        firstFieldDefElement = fieldDefElement;
      }
      lastFieldDefElement = fieldDefElement;
    }
  
    return firstFieldDefElement;
  };
  
  /**
   * Constructs a <field> element that describes the provided Blockly.Field.
   * @param {!Blockly.Field} field The field from which the definition is copied.
   * @param {!Element} A <field> for the Field definition.
   * @private
   */
  BlockDefinitionExtractor.buildFieldElement_ = function(field) {
    if (field instanceof Blockly.FieldLabel) {
      return BlockDefinitionExtractor.buildFieldLabel_(field.text_);
    } else if (field instanceof Blockly.FieldTextInput) {
       return BlockDefinitionExtractor.buildFieldInput_(field.name, field.text_);
    } else if (field instanceof Blockly.FieldNumber) {
      return BlockDefinitionExtractor.buildFieldNumber_(
          field.name, field.text_, field.min_, field.max_, field.presicion_);
    } else if (field instanceof Blockly.FieldAngle) {
      return BlockDefinitionExtractor.buildFieldAngle_(field.name, field.text_);
    } else if (field instanceof Blockly.FieldCheckbox) {
      return BlockDefinitionExtractor.buildFieldCheckbox_(field.name, field.state_);
    } else if (field instanceof Blockly.FieldColour) {
      return BlockDefinitionExtractor.buildFieldColour_(field.name, field.colour_);
    } else if (field instanceof Blockly.FieldImage) {
      return BlockDefinitionExtractor.buildFieldImage_(
          field.src_, field.width_, field.height_, field.text_);
    } else if (field instanceof Blockly.FieldVariable) {
      // FieldVariable must be before FieldDropdown, because FieldVariable is a
      // subclass.
      return BlockDefinitionExtractor.buildFieldVariable_(field.name, field.text_);
    } else if (field instanceof Blockly.FieldDropdown) {
      return BlockDefinitionExtractor.buildFieldDropdown_(field);
    }
    throw Error('Unrecognized field class: ' + field.constructor.name);
  };
  
  
  /**
   * Creates a <block> element representing a FieldLabel definition.
   * @param {string} text
   * @return {Element} The XML for FieldLabel definition.
   * @private
   */
  BlockDefinitionExtractor.buildFieldLabel_ = function(text) {
    var fieldBlock =
        BlockDefinitionExtractor.newDomElement_('block', {type: 'field_static'});
    fieldBlock.append(
        BlockDefinitionExtractor.newDomElement_('field', {name: 'TEXT'}, text));
    return fieldBlock;
  };
  
  /**
   * Creates a <block> element representing a FieldInput (text input) definition.
   *
   * @param {string} fieldName The identifying name of the field.
   * @param {string} text The default text string.
   * @return {Element} The XML for FieldInput definition.
   * @private
   */
  BlockDefinitionExtractor.buildFieldInput_ = function(fieldName, text) {
    var fieldInput =
        BlockDefinitionExtractor.newDomElement_('block', {type: 'field_input'});
    fieldInput.append(
        BlockDefinitionExtractor.newDomElement_('field', {name: 'TEXT'}, text));
    fieldInput.append(BlockDefinitionExtractor.newDomElement_(
        'field', {name: 'FIELDNAME'}, fieldName));
    return fieldInput;
  };
  
  /**
   * Creates a <block> element representing a FieldNumber definition.
   *
   * @param {string} fieldName The identifying name of the field.
   * @param {number} value The field's default value.
   * @param {number} min The minimum allowed value, or negative infinity.
   * @param {number} max The maximum allowed value, or positive infinity.
   * @param {number} precision The precision allowed for the number.
   * @return {Element} The XML for FieldNumber definition.
   * @private
   */
  BlockDefinitionExtractor.buildFieldNumber_ =
    function(fieldName, value, min, max, precision)
  {
    var fieldNumber =
        BlockDefinitionExtractor.newDomElement_('block', {type: 'field_number'});
    fieldNumber.append(BlockDefinitionExtractor.newDomElement_(
        'field', {name: 'VALUE'}, value));
    fieldNumber.append(BlockDefinitionExtractor.newDomElement_(
        'field', {name: 'FIELDNAME'}, fieldName));
    fieldNumber.append(BlockDefinitionExtractor.newDomElement_(
        'field', {name: 'MIN'}, min));
    fieldNumber.append(BlockDefinitionExtractor.newDomElement_(
        'field', {name: 'MAX'}, max));
    fieldNumber.append(BlockDefinitionExtractor.newDomElement_(
        'field', {name: 'PRECISION'}, precision));
    return fieldNumber;
  };
  
  /**
   * Creates a <block> element representing a FieldAngle definition.
   *
   * @param {string} fieldName The identifying name of the field.
   * @param {number} angle The field's default value.
   * @return {Element} The XML for FieldAngle definition.
   * @private
   */
  BlockDefinitionExtractor.buildFieldAngle_ = function(angle, fieldName) {
    var fieldAngle =
        BlockDefinitionExtractor.newDomElement_('block', {type: 'field_angle'});
    fieldAngle.append(BlockDefinitionExtractor.newDomElement_(
        'field', {name: 'ANGLE'}, angle));
    fieldAngle.append(BlockDefinitionExtractor.newDomElement_(
        'field', {name: 'FIELDNAME'}, fieldName));
    return fieldAngle;
  };
  
  /**
   * Creates a <block> element representing a FieldDropdown definition.
   *
   * @param {Blockly.FieldDropdown} dropdown
   * @return {Element} The <block> element representing a similar FieldDropdown.
   * @private
   */
  BlockDefinitionExtractor.buildFieldDropdown_ = function(dropdown) {
    var menuGenerator = dropdown.menuGenerator_;
    if (typeof menuGenerator === 'function') {
      var options = menuGenerator();
    } else if (goog.isArray(menuGenerator)) {
      var options = menuGenerator;
    } else {
      throw new Error('Unrecognized type of menuGenerator: ' + menuGenerator);
    }
  
    var fieldDropdown = BlockDefinitionExtractor.newDomElement_(
        'block', {type: 'field_dropdown'});
    var optionsStr = '[';
  
    var mutation = BlockDefinitionExtractor.newDomElement_('mutation');
    fieldDropdown.append(mutation);
    fieldDropdown.append(BlockDefinitionExtractor.newDomElement_(
        'field', {name: 'FIELDNAME'}, dropdown.name));
    for (var i=0; i<options.length; i++) {
      var option = options[i];
      if (typeof option[0] === "string") {
        optionsStr += '"text",'
        fieldDropdown.append(BlockDefinitionExtractor.newDomElement_(
            'field', {name: 'USER'+i}, option[0]));
      } else {
        optionsStr += '"image",';
        fieldDropdown.append(
            BlockDefinitionExtractor.newDomElement_(
            'field', {name: 'SRC'+i}, option[0].src));
        fieldDropdown.append(BlockDefinitionExtractor.newDomElement_(
            'field', {name: 'WIDTH'+i}, option[0].width));
        fieldDropdown.append(BlockDefinitionExtractor.newDomElement_(
            'field', {name: 'HEIGHT'+i}, option[0].height));
        fieldDropdown.append(BlockDefinitionExtractor.newDomElement_(
            'field', {name: 'ALT'+i}, option[0].alt));
      }
      fieldDropdown.append(BlockDefinitionExtractor.newDomElement_(
          'field', {name: 'CPU'+i}, option[1]));
    }
    optionsStr = optionsStr.slice(0,-1); // Drop last comma
    optionsStr += ']';
    mutation.setAttribute('options', optionsStr);
  
    return fieldDropdown;
  };
  
  /**
   * Creates a <block> element representing a FieldCheckbox definition.
   *
   * @param {string} fieldName The identifying name of the field.
   * @param {string} checked The field's default value, true or false.
   * @return {Element} The XML for FieldCheckbox definition.
   * @private
   */
  BlockDefinitionExtractor.buildFieldCheckbox_ =
    function(fieldName, checked)
  {
    var fieldCheckbox = BlockDefinitionExtractor.newDomElement_(
        'block', {type: 'field_checkbox'});
    fieldCheckbox.append(BlockDefinitionExtractor.newDomElement_(
        'field', {name: 'CHECKED'}, checked));
    fieldCheckbox.append(BlockDefinitionExtractor.newDomElement_(
        'field', {name: 'FIELDNAME'}, fieldName));
    return fieldCheckbox;
  };
  
  /**
   * Creates a <block> element representing a FieldColour definition.
   *
   * @param {string} fieldName The identifying name of the field.
   * @param {string} colour The field's default value as a string.
   * @return {Element} The XML for FieldColour definition.
   * @private
   */
  BlockDefinitionExtractor.buildFieldColour_ =
      function(fieldName, colour)
  {
    var fieldColour = BlockDefinitionExtractor.newDomElement_(
        'block', {type: 'field_colour'});
    fieldColour.append(BlockDefinitionExtractor.newDomElement_(
        'field', {name: 'COLOUR'}, colour));
    fieldColour.append(BlockDefinitionExtractor.newDomElement_(
        'field', {name: 'FIELDNAME'}, fieldName));
    return fieldColour;
  };
  
  /**
   * Creates a <block> element representing a FieldVaraible definition.
   *
   * @param {string} fieldName The identifying name of the field.
   * @param {string} varName The variables
   * @return {Element} The <block> element representing the FieldVariable.
   * @private
   */
  BlockDefinitionExtractor.buildFieldVariable_ = function(fieldName, varName) {
    var fieldVar = BlockDefinitionExtractor.newDomElement_(
        'block', {type: 'field_variable'});
    fieldVar.append(BlockDefinitionExtractor.newDomElement_(
        'field', {name: 'FIELDNAME'}, fieldName));
    fieldVar.append(BlockDefinitionExtractor.newDomElement_(
        'field', {name: 'TEXT'}, varName));
    return fieldVar;
  };
  
  /**
   * Creates a <block> element representing a FieldImage definition.
   *
   * @param {string} src The URL of the field image.
   * @param {number} width The pixel width of the source image
   * @param {number} height The pixel height of the source image.
   * @param {string} alt Alterante text to describe image.
   * @private
   */
  BlockDefinitionExtractor.buildFieldImage_ =
    function(src, width, height, alt)
  {
    var block1 = BlockDefinitionExtractor.newDomElement_(
        'block', {type: 'field_image'});
    block1.append(BlockDefinitionExtractor.newDomElement_(
        'field', {name: 'SRC'}, src));
    block1.append(BlockDefinitionExtractor.newDomElement_(
        'field', {name: 'WIDTH'}, width));
    block1.append(BlockDefinitionExtractor.newDomElement_(
        'field', {name: 'HEIGHT'}, height));
    block1.append(BlockDefinitionExtractor.newDomElement_(
        'field', {name: 'ALT'}, alt));
  };
  
  /**
   * Creates a <block> element a group of allowed connection constraint types.
   *
   * @param {Array<string>} types List of type names in this group.
   * @return {Element} The <block> element representing the group, with child
   *     types attached.
   * @private
   */
  BlockDefinitionExtractor.typeGroup_ = function(types) {
    var typeGroupBlock = BlockDefinitionExtractor.newDomElement_(
        'block', {type: 'type_group'});
    typeGroupBlock.append(BlockDefinitionExtractor.newDomElement_(
        'mutation', {types:types.length}));
    for (var i=0; i<types.length; i++) {
      var typeBlock = BlockDefinitionExtractor.buildBlockForType_(types[i]);
      var valueBlock = BlockDefinitionExtractor.newDomElement_(
          'value', {name:'TYPE'+i});
      valueBlock.append(typeBlock);
      typeGroupBlock.append(valueBlock);
    }
    return typeGroupBlock;
  };
  
  /**
   * Creates a <shadow> block element representing the default null connection
   * constraint.
   * @return {Element} The <block> element representing the "null" type
   *     constraint.
   * @private
   */
  BlockDefinitionExtractor.typeNullShadow_ = function() {
    return BlockDefinitionExtractor.newDomElement_(
        'shadow', {type: 'type_null'});
  };
  
  /**
   * Creates a <block> element representing null in a connection constraint.
   * @return {Element} The <block> element representing the "null" type
   *     constraint.
   * @private
   */
  BlockDefinitionExtractor.typeNull_ = function() {
    return BlockDefinitionExtractor.newDomElement_('block', {type: 'type_null'});
  };
  
  /**
   * Creates a <block> element representing the a boolean in a connection
   * constraint.
   * @return {Element} The <block> element representing the "boolean" type
   *     constraint.
   * @private
   */
  BlockDefinitionExtractor.typeBoolean_ = function() {
    return BlockDefinitionExtractor.newDomElement_(
        'block', {type: 'type_boolean'});
  };
  
  /**
   * Creates a <block> element representing the a number in a connection
   * constraint.
   * @return {Element} The <block> element representing the "number" type
   *     constraint.
   * @private
   */
  BlockDefinitionExtractor.typeNumber_ = function() {
    return BlockDefinitionExtractor.newDomElement_(
        'block', {type: 'type_number'});
  };
  
  /**
   * Creates a <block> element representing the a string in a connection
   * constraint.
   * @return {Element} The <block> element representing the "string" type
   *     constraint.
   * @private
   */
  BlockDefinitionExtractor.typeString_ = function() {
    return BlockDefinitionExtractor.newDomElement_(
        'block', {type: 'type_string'});
  };
  
  /**
   * Creates a <block> element representing the a list in a connection
   * constraint.
   * @return {Element} The <block> element representing the "list" type
   *     constraint.
   * @private
   */
  BlockDefinitionExtractor.typeList_ = function() {
    return BlockDefinitionExtractor.newDomElement_('block', {type: 'type_list'});
  };
  
  /**
   * Creates a <block> element representing the given custom connection
   * constraint type name.
   *
   * @param {string} type The connection constratin type name.
   * @return {Element} The <block> element representing a custom input type
   *     constraint.
   * @private
   */
  BlockDefinitionExtractor.typeOther_ = function(type) {
    var block = BlockDefinitionExtractor.newDomElement_(
        'block', {type: 'type_other'});
    block.append(BlockDefinitionExtractor.newDomElement_(
        'field', {name: 'TYPE'}, type));
    return block;
  };
  
  /**
   * Creates a block Element for the color_hue block, with the given hue.
   * @param hue {number} The hue value, from 0 to 360.
   * @return {Element} The <block> Element representing a colour_hue block
   *     with the given hue.
   * @private
   */
  BlockDefinitionExtractor.colourBlockFromHue_ = function(hue) {
    var colourBlock = BlockDefinitionExtractor.newDomElement_(
        'block', {type: 'colour_hue'});
    colourBlock.append(BlockDefinitionExtractor.newDomElement_('mutation', {
      colour: Blockly.hueToRgb(hue)
    }));
    colourBlock.append(BlockDefinitionExtractor.newDomElement_(
        'field', {name: 'HUE'}, hue.toString()));
    return colourBlock;
  };
  
  /**
   * Creates a block Element for a text block with the given text.
   *
   * @param text {string} The text value of the block.
   * @return {Element} The <block> element representing a "text" block.
   * @private
   */
  BlockDefinitionExtractor.text_ = function(text) {
    var textBlock =
        BlockDefinitionExtractor.newDomElement_('block', {type: 'text'});
    if (text) {
      textBlock.append(BlockDefinitionExtractor.newDomElement_(
        'field', {name: 'TEXT'}, text));
    } // Else, use empty string default.
    return textBlock;
  };
  