/**
 * @license
 * Copyright 2019 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview A field used to customize a turtle.
 * @author bekawestberg@gmail.com (Beka Westberg)
 */
'use strict';

// You must provide the constructor for your custom field.
goog.provide('CustomFields.FieldTurtle');

// You must require the abstract field class to inherit from.
goog.require('Blockly.Field');
goog.require('Blockly.fieldRegistry');
goog.require('Blockly.utils');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.object');
goog.require('Blockly.utils.Size');

var CustomFields = CustomFields || {};

// Generally field's values should be optional, and have logical defaults.
// If this is not possible (for example image fields can't have logical
// defaults) the field should throw a clear error when a value is not provided.
// Editable fields also generally accept validators, so we will accept a
// validator.
CustomFields.FieldTurtle = function(
    opt_pattern, opt_hat, opt_turtleName, opt_validator) {

  // The turtle field contains an object as its value, so we need to compile
  // the parameters into an object.
  var value = {};
  value.pattern = opt_pattern || CustomFields.FieldTurtle.PATTERNS[0];
  value.hat = opt_hat || CustomFields.FieldTurtle.HATS[0];
  value.turtleName = opt_turtleName || CustomFields.FieldTurtle.NAMES[0];

  // A field constructor should always call its parent constructor, because
  // that helps keep the code organized and DRY.
  CustomFields.FieldTurtle.superClass_.constructor.call(
      this, value, opt_validator);

  /**
   * The size of the area rendered by the field.
   * @type {Blockly.utils.Size}
   * @protected
   * @override
   */
  this.size_ = new Blockly.utils.Size(0, 0);
};
Blockly.utils.object.inherits(CustomFields.FieldTurtle, Blockly.Field);

// This allows the field to be constructed using a JSON block definition.
CustomFields.FieldTurtle.fromJson = function(options) {
  // In this case we simply pass the JSON options along to the constructor,
  // but you can also use this to get message references, and other such things.
  return new CustomFields.FieldTurtle(
    options['pattern'],
    options['hat'],
    options['turtleName']);
};

// Since this field is editable we must also define serializable as true
// (for backwards compatibility reasons serializable is false by default).
CustomFields.FieldTurtle.prototype.SERIALIZABLE = true;

// The cursor property defines what the mouse will look like when the user
// hovers over the field. By default the cursor will be whatever
// .blocklyDraggable's cursor is defined as (vis. grab). Most fields define
// this property as 'default'.
CustomFields.FieldTurtle.prototype.CURSOR = 'pointer';

// How far to move the text to keep it to the right of the turtle.
// May change if the turtle gets fancy enough.
CustomFields.FieldTurtle.prototype.TEXT_OFFSET_X = 80;

// Padding that the border rect adds around the turtle and its name.
CustomFields.FieldTurtle.prototype.PADDING = Blockly.Field.X_PADDING;

// These are the different options for our turtle. Being declared this way
// means they are static, and not translatable. If you want to do something
// similar, but make it translatable you should set up your options like a
// dropdown field, with language-neutral keys and human-readable values.
CustomFields.FieldTurtle.PATTERNS =
    ['Dots', 'Stripes', 'Hexagons'];
CustomFields.FieldTurtle.HATS =
    ['Stovepipe', 'Crown', 'Propeller', 'Mask', 'Fedora'];
CustomFields.FieldTurtle.NAMES =
    ['Yertle', 'Franklin', 'Crush', 'Leonardo', 'Bowser', 'Squirtle', 'Oogway'];

// Used to keep track of our editor event listeners, so they can be
// properly disposed of when the field closes. You can keep track of your
// listeners however you want, just be sure to dispose of them!
CustomFields.FieldTurtle.prototype.editorListeners_ = [];

// Used to create the DOM of our field.
CustomFields.FieldTurtle.prototype.initView = function() {
  // Because we want to have both a borderRect_ (background) and a
  // textElement_ (text) we can call the super-function. If we only wanted
  // one or the other, we could call their individual createX functions.
  CustomFields.FieldTurtle.superClass_.initView.call(this);

  // Note that the field group is created by the abstract field's init_
  // function. This means that *all elements* should be children of the
  // fieldGroup_.
  this.createView_();
};

// Updates how the field looks depending on if it is editable or not.
CustomFields.FieldTurtle.prototype.updateEditable = function() {
  if (!this.fieldGroup_) {
    // Not initialized yet.
    return;
  }
  // The default functionality just makes it so the borderRect_ does not
  // highlight when hovered.
  Blockly.FieldColour.superClass_.updateEditable.call(this);
  // Things like this are best applied to the clickTarget_. By default the
  // click target is the same as getSvgRoot, which by default is the
  // fieldGroup_.
  var group = this.getClickTarget_();
  if (!this.isCurrentlyEditable()) {
    group.style.cursor = 'not-allowed';
  } else {
    group.style.cursor = this.CURSOR;
  }
};

// Gets the text to display when the block is collapsed
CustomFields.FieldTurtle.prototype.getText = function() {
  var text = this.value_.turtleName + ' wearing a ' + this.value_.hat;
  if (this.value_.hat == 'Stovepipe' || this.value_.hat == 'Propeller') {
    text += ' hat';
  }
  return text;
};

// Makes sure new field values (given to setValue) are valid, meaning
// something this field can legally "hold". Class validators can either change
// the input value, or return null if the input value is invalid. Called by
// the setValue() function.
CustomFields.FieldTurtle.prototype.doClassValidation_ = function(newValue) {
  // Undefined signals that we want the value to remain unchanged. This is a
  // special feature of turtle fields, but could be useful for other
  // multi-part fields.
  if (newValue.pattern == undefined) {
    newValue.pattern = this.displayValue_ && this.displayValue_.pattern;
  // We only want to allow patterns that are part of our pattern list.
  // Anything else is invalid, so we return null.
  } else if (CustomFields.FieldTurtle.PATTERNS.indexOf(newValue.pattern) == -1) {
    newValue.pattern = null;
  }

  if (newValue.hat == undefined) {
    newValue.hat = this.displayValue_ && this.displayValue_.hat;
  } else if (CustomFields.FieldTurtle.HATS.indexOf(newValue.hat) == -1) {
    newValue.hat = null;
  }

  if (newValue.turtleName == undefined) {
    newValue.turtleName = this.displayValue_ && this.displayValue_.turtleName;
  } else if (CustomFields.FieldTurtle.NAMES.indexOf(newValue.turtleName) == -1) {
    newValue.turtleName = null;
  }

  // This is a strategy for dealing with defaults on multi-part values.
  // The class validator sets individual properties of the object to null
  // to indicate that they are invalid, and then caches that object to the
  // cachedValidatedValue_ property. This way the field can, for
  // example, properly handle an invalid pattern, combined with a valid hat.
  // This can also be done with local validators.
  this.cachedValidatedValue_ = newValue;

  // Always be sure to return!
  if (!newValue.pattern || !newValue.hat || !newValue.turtleName) {
    return null;
  }
  return newValue;
};

// Saves the new field value. Called by the setValue function.
CustomFields.FieldTurtle.prototype.doValueUpdate_ = function(newValue) {
  // The default function sets this field's this.value_ property to the
  // newValue, and its this.isDirty_ property to true. The isDirty_ property
  // tells the setValue function whether the field needs to be re-rendered.
  CustomFields.FieldTurtle.superClass_.doValueUpdate_.call(this, newValue);
  this.displayValue_ = newValue;
  // Since this field has custom UI for invalid values, we also want to make
  // sure it knows it is now valid.
  this.isValueInvalid_ = false;
};

// Notifies that the field that the new value was invalid. Called by
// setValue function. Can either be triggered by the class validator, or the
// local validator.
CustomFields.FieldTurtle.prototype.doValueInvalid_ = function(invalidValue) {
  // By default this function is no-op, meaning if the new value is invalid
  // the field simply won't be updated. This field has custom UI for invalid
  // values, so we override this function.

  // We want the value to be displayed like normal.
  // But we want to flag it as invalid, so the render_ function knows to
  // make the borderRect_ red.
  this.displayValue_ = invalidValue;
  this.isDirty_ = true;
  this.isValueInvalid_ = true;
};

// Updates the field's on-block display based on the current display value.
CustomFields.FieldTurtle.prototype.render_ = function() {
  var value = this.displayValue_;

  // Always do editor updates inside render. This makes sure the editor
  // always displays the correct value, even if a validator changes it.
  if (this.editor_) {
    this.renderEditor_();
  }

  this.stovepipe_.style.display = 'none';
  this.crown_.style.display = 'none';
  this.mask_.style.display = 'none';
  this.propeller_.style.display = 'none';
  this.fedora_.style.display = 'none';
  switch(value.hat) {
    case 'Stovepipe':
      this.stovepipe_.style.display = '';
      this.turtleGroup_.setAttribute('transform', 'translate(0,12)');
      this.textElement_.setAttribute(
          'transform', 'translate(' + this.TEXT_OFFSET_X + ',20)');
      break;
    case 'Crown':
      this.crown_.style.display = '';
      this.turtleGroup_.setAttribute('transform', 'translate(0,9)');
      this.textElement_.setAttribute(
          'transform', 'translate(' + this.TEXT_OFFSET_X + ',16)');
      break;
    case 'Mask':
      this.mask_.style.display = '';
      this.turtleGroup_.setAttribute('transform', 'translate(0,1.2)');
      this.textElement_.setAttribute('transform',
          'translate(' + this.TEXT_OFFSET_X + ',4)');
      break;
    case 'Propeller':
      this.propeller_.style.display = '';
      this.turtleGroup_.setAttribute('transform', 'translate(0,6)');
      this.textElement_.setAttribute('transform',
          'translate(' + this.TEXT_OFFSET_X + ',12)');
      break;
    case 'Fedora':
      this.fedora_.style.display = '';
      this.turtleGroup_.setAttribute('transform', 'translate(0,6)');
      this.textElement_.setAttribute('transform',
          'translate(' + this.TEXT_OFFSET_X + ',12)');
      break;
  }

  switch(value.pattern) {
    case 'Dots':
      this.shellPattern_.setAttribute('fill', 'url(#polkadots)');
      break;
    case 'Stripes':
      this.shellPattern_.setAttribute('fill', 'url(#stripes)');
      break;
    case 'Hexagons':
      this.shellPattern_.setAttribute('fill', 'url(#hexagons)');
      break;
  }

  // Always modify the textContent_ rather than the textElement_. This
  // allows fields to append DOM to the textElement (e.g. the angle field).
  this.textContent_.nodeValue = value.turtleName;

  if (this.isValueInvalid_) {
    this.borderRect_.style.fill = '#f99';
    this.borderRect_.style.fillOpacity = 1;
  } else {
    this.borderRect_.style.fill = '#fff';
    this.borderRect_.style.fillOpacity = 0.6;
  }

  this.updateSize_();
};

CustomFields.FieldTurtle.prototype.renderEditor_ = function() {
  var value = this.displayValue_;

  // .textElement is a property assigned to the element.
  // It allows the text to be edited without destroying the warning icon.
  this.editor_.patternText.textElement.nodeValue = value.pattern;
  this.editor_.hatText.textElement.nodeValue = value.hat;
  this.editor_.turtleNameText.textElement.nodeValue = value.turtleName;

  this.editor_.patternText.warningIcon.style.display =
    this.cachedValidatedValue_.pattern ? 'none' : '';
  this.editor_.hatText.warningIcon.style.display =
    this.cachedValidatedValue_.hat ? 'none' : '';
  this.editor_.turtleNameText.warningIcon.style.display =
    this.cachedValidatedValue_.turtleName ? 'none' : '';
};

// Used to update the size of the field. This function's logic could be simply
// included inside render_ (it is not called anywhere else), but it is
// usually separated to keep code more organized.
CustomFields.FieldTurtle.prototype.updateSize_ = function() {
  var bbox = this.movableGroup_.getBBox();
  var width = bbox.width;
  var height = bbox.height;
  if (this.borderRect_) {
    width += this.PADDING;
    height += this.PADDING;
    this.borderRect_.setAttribute('width', width);
    this.borderRect_.setAttribute('height', height);
  }
  // Note how both the width and the height can be dynamic.
  this.size_.width = width;
  this.size_.height = height;
};

// Called when the field is clicked. It is usually used to show an editor,
// but it can also be used for other things e.g. the checkbox field uses
// this function to check/uncheck itself.
CustomFields.FieldTurtle.prototype.showEditor_ = function() {
  this.editor_ = this.dropdownCreate_();
  this.renderEditor_();
  Blockly.DropDownDiv.getContentDiv().appendChild(this.editor_);

  // These allow us to have the editor match the block's colour.
  var fillColour = this.sourceBlock_.getColour();
  // This is technically a package function, meaning it could change.
  var borderColours = this.sourceBlock_.getColourBorder();
  var borderColour = borderColours.colourBorder || borderColours.colourDark;
  Blockly.DropDownDiv.setColour(fillColour, borderColour);

  // Always pass the dropdown div a dispose function so that you can clean
  // up event listeners when the editor closes.
  Blockly.DropDownDiv.showPositionedByField(
      this, this.dropdownDispose_.bind(this));
};

// Creates the UI of the editor, and adds event listeners to it.
CustomFields.FieldTurtle.prototype.dropdownCreate_ = function() {
  var createRow = function(table) {
    var row = table.appendChild(document.createElement('tr'));
    row.className = 'row';
    return row;
  };
  var createLeftArrow = function(row) {
    var cell = document.createElement('div');
    cell.className = 'arrow';
    var leftArrow = document.createElement('button');
    leftArrow.setAttribute('type', 'button');
    leftArrow.textContent = '<';
    cell.appendChild(leftArrow);
    row.appendChild(cell);
    return cell;
  };
  var createTextNode = function(row, text) {
    var cell = document.createElement('div');
    cell.className = 'text';
    var text = document.createTextNode(text);
    cell.appendChild(text);
    cell.textElement = text;
    var warning = document.createElement('img');
    warning.setAttribute('src', 'media/warning.svg');
    warning.setAttribute('height', '16px');
    warning.setAttribute('width', '16px');
    warning.style.marginLeft = '4px';
    cell.appendChild(warning);
    cell.warningIcon = warning;
    row.appendChild(cell);
    return cell;
  };
  var createRightArrow = function(row) {
    var cell = document.createElement('div');
    cell.className = 'arrow';
    var rightArrow = document.createElement('button');
    rightArrow.setAttribute('type', 'button');
    rightArrow.textContent = '>';
    cell.appendChild(rightArrow);
    row.appendChild(cell);
    return cell;
  };
  var createArrowListener = function(variable, array, direction) {
    return function() {
      var currentIndex = array.indexOf(this.displayValue_[variable]);
      currentIndex += direction;
      if (currentIndex <= -1) {
        currentIndex = array.length - 1;
      } else if (currentIndex >= array.length) {
        currentIndex = 0;
      }
      var value = {};
      value[variable] = array[currentIndex];
      this.setValue(value);
    };
  };

  var widget = document.createElement('div');
  widget.className = 'customFieldsTurtleWidget blocklyNonSelectable';

  var table = document.createElement('div');
  table.className = 'table';
  widget.appendChild(table);

  var row = createRow(table);
  var leftArrow = createLeftArrow(row);
  widget.patternText = createTextNode(row, this.displayValue_.pattern);
  var rightArrow = createRightArrow(row);
  this.editorListeners_.push(Blockly.bindEvent_(leftArrow, 'mouseup', this,
      createArrowListener('pattern', CustomFields.FieldTurtle.PATTERNS, -1)));
  this.editorListeners_.push(Blockly.bindEvent_(rightArrow, 'mouseup', this,
      createArrowListener('pattern', CustomFields.FieldTurtle.PATTERNS, 1)));

  row = createRow(table);
  leftArrow = createLeftArrow(row);
  widget.hatText = createTextNode(row, this.displayValue_.hat);
  rightArrow = createRightArrow(row);
  this.editorListeners_.push(Blockly.bindEvent_(leftArrow, 'mouseup', this,
      createArrowListener('hat', CustomFields.FieldTurtle.HATS, -1)));
  this.editorListeners_.push(Blockly.bindEvent_(rightArrow, 'mouseup', this,
      createArrowListener('hat', CustomFields.FieldTurtle.HATS, 1)));

  row = createRow(table);
  leftArrow = createLeftArrow(row);
  widget.turtleNameText = createTextNode(row, this.displayValue_.turtleName);
  rightArrow = createRightArrow(row);
  this.editorListeners_.push(Blockly.bindEvent_(leftArrow, 'mouseup', this,
      createArrowListener('turtleName', CustomFields.FieldTurtle.NAMES, -1)));
  this.editorListeners_.push(Blockly.bindEvent_(rightArrow, 'mouseup', this,
      createArrowListener('turtleName', CustomFields.FieldTurtle.NAMES, 1)));

  var randomizeButton = document.createElement('button');
  randomizeButton.className = 'randomize';
  randomizeButton.setAttribute('type', 'button');
  randomizeButton.textContent = 'randomize turtle';
  this.editorListeners_.push(Blockly.bindEvent_(randomizeButton, 'mouseup', this,
    function() {
    var value = {};
    value.pattern = CustomFields.FieldTurtle.PATTERNS[
        Math.floor(Math.random() * CustomFields.FieldTurtle.PATTERNS.length)];

    value.hat = CustomFields.FieldTurtle.HATS[
      Math.floor(Math.random() * CustomFields.FieldTurtle.HATS.length)];

    value.turtleName = CustomFields.FieldTurtle.NAMES[
      Math.floor(Math.random() * CustomFields.FieldTurtle.NAMES.length)];

    this.setValue(value);
  }));
  widget.appendChild(randomizeButton);

  return widget;
};

// Cleans up any event listeners that were attached to the now hidden editor.
CustomFields.FieldTurtle.prototype.dropdownDispose_ = function() {
  for (var i = this.editorListeners_.length, listener;
      listener = this.editorListeners_[i]; i--) {
    Blockly.unbindEvent_(listener);
    this.editorListeners_.pop();
  }
};

// Updates the field's colour based on the colour of the block. Called by
// block.updateColour.
CustomFields.FieldTurtle.prototype.updateColour = function() {
  if (!this.sourceBlock_) {
    return;
  }
  // The getColourX functions are the best way to access the colours of a block.
  var isShadow = this.sourceBlock_.isShadow();
  var fillColour = isShadow  ?
      this.sourceBlock_.getColourShadow() : this.sourceBlock_.getColour();
  // This is technically a package function, meaning it could change.
  var borderColours = this.sourceBlock_.getColourBorder();
  var borderColour = isShadow ? fillColour :
      borderColours.colourBorder || borderColours.colourDark;

  var child = this.turtleGroup_.firstChild;
  while(child) {
    // If it is a text node, continue.
    if (child.nodeType == 3) {
      child = child.nextSibling;
      continue;
    }
    // Or if it is a non-turtle node, continue.
    var className = child.getAttribute('class');
    if (!className || className.indexOf('turtleBody') == -1) {
      child = child.nextSibling;
      continue;
    }

    child.style.fill = fillColour;
    child.style.stroke = borderColour;
    child = child.nextSibling;
  }
};

// Saves the field's value to an XML node. Allows for custom serialization.
CustomFields.FieldTurtle.prototype.toXml = function(fieldElement) {
  // The default implementation of this function creates a node that looks
  // like this: (where value is returned by getValue())
  // <field name="FIELDNAME">value</field>
  // But this doesn't work for our field because it stores an /object/.

  fieldElement.setAttribute('pattern', this.value_.pattern);
  fieldElement.setAttribute('hat', this.value_.hat);
  // The textContent usually contains whatever is closest to the field's
  // 'value'. The textContent doesn't need to contain anything, but saving
  // something to it does aid in readability.
  fieldElement.textContent = this.value_.turtleName;

  // Always return the element!
  return fieldElement;
};

// Sets the field's value based on an XML node. Allows for custom
// de-serialization.
CustomFields.FieldTurtle.prototype.fromXml = function(fieldElement) {
  // Because we had to do custom serialization for this field, we also need
  // to do custom de-serialization.

  var value = {};
  value.pattern = fieldElement.getAttribute('pattern');
  value.hat = fieldElement.getAttribute('hat');
  value.turtleName = fieldElement.textContent;
  // The end goal is to call this.setValue()
  this.setValue(value);
};

// Blockly needs to know the JSON name of this field. Usually this is
// registered at the bottom of the field class.
Blockly.fieldRegistry.register('field_turtle', CustomFields.FieldTurtle);

// Called by initView to create all of the SVGs. This is just used to keep
// the code more organized.
CustomFields.FieldTurtle.prototype.createView_ = function() {
  this.movableGroup_ = Blockly.utils.dom.createSvgElement('g',
    {
      'transform': 'translate(0,5)'
    }, this.fieldGroup_);
  var scaleGroup = Blockly.utils.dom.createSvgElement('g',
    {
      'transform': 'scale(1.5)'
    }, this.movableGroup_);
  this.turtleGroup_ = Blockly.utils.dom.createSvgElement('g',
    {
      // Makes the smaller turtle graphic align with the hats.
      'class': 'turtleBody'
    }, scaleGroup);
  var tail = Blockly.utils.dom.createSvgElement('path',
    {
      'class': 'turtleBody',
      'd': 'M7,27.5H0.188c3.959-2,6.547-2.708,8.776-5.237',
      'transform': 'translate(0.312 -12.994)'
    }, this.turtleGroup_);
  var legLeft = Blockly.utils.dom.createSvgElement('rect',
    {
      'class': 'turtleBody',
      'x': 8.812,
      'y': 12.506,
      'width': 4,
      'height': 10
    }, this.turtleGroup_);
  var legRight = Blockly.utils.dom.createSvgElement('rect',
    {
      'class': 'turtleBody',
      'x': 28.812,
      'y': 12.506,
      'width': 4,
      'height': 10
    }, this.turtleGroup_);
  var head = Blockly.utils.dom.createSvgElement('path',
    {
      'class': 'turtleBody',
      'd': 'M47.991,17.884c0,1.92-2.144,3.477-4.788,3.477a6.262,6.262,0,0,1-2.212-.392c-0.2-.077-1.995,2.343-4.866,3.112a17.019,17.019,0,0,1-6.01.588c-4.413-.053-2.5-3.412-2.745-3.819-0.147-.242,2.232.144,6.126-0.376a7.392,7.392,0,0,0,4.919-2.588c0-1.92,2.144-3.477,4.788-3.477S47.991,15.964,47.991,17.884Z',
      'transform': 'translate(0.312 -12.994)'
    }, this.turtleGroup_);
  var smile = Blockly.utils.dom.createSvgElement('path',
    {
      'class': 'turtleBody',
      'd': 'M42.223,18.668a3.614,3.614,0,0,0,2.728,2.38',
      'transform': 'translate(0.312 -12.994)'
    }, this.turtleGroup_);
  var sclera = Blockly.utils.dom.createSvgElement('ellipse',
    {
      'cx': 43.435,
      'cy': 2.61,
      'rx': 2.247,
      'ry': 2.61,
      'fill': '#fff'
    }, this.turtleGroup_);
  var pupil = Blockly.utils.dom.createSvgElement('ellipse',
    {
      'cx': 44.166,
      'cy': 3.403,
      'rx': 1.318,
      'ry': 1.62
    }, this.turtleGroup_);
  var shell = Blockly.utils.dom.createSvgElement('path',
    {
      'class': 'turtleBody',
      'd': 'M33.4,27.5H7.193c0-6,5.866-13.021,13.1-13.021S33.4,21.5,33.4,27.5Z',
      'transform': 'translate(0.312 -12.994)'
    }, this.turtleGroup_);
  this.shellPattern_ = Blockly.utils.dom.createSvgElement('path',
    {
      'd': 'M33.4,27.5H7.193c0-6,5.866-13.021,13.1-13.021S33.4,21.5,33.4,27.5Z',
      'transform': 'translate(0.312 -12.994)'
    }, this.turtleGroup_);

  this.stovepipe_ = Blockly.utils.dom.createSvgElement('image',
    {
      'width': '50',
      'height': '18'
    }, scaleGroup);
  this.stovepipe_.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href',
    'media/stovepipe.svg');
  this.crown_ = Blockly.utils.dom.createSvgElement('image',
    {
      'width': '50',
      'height': '15'
    }, scaleGroup);
  this.crown_.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href',
    'media/crown.svg');
  this.mask_ = Blockly.utils.dom.createSvgElement('image',
    {
      'width': '50',
      'height': '14'
    }, scaleGroup);
  this.mask_.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href',
    'media/mask.svg');
  this.propeller_ = Blockly.utils.dom.createSvgElement('image',
    {
      'width': '50',
      'height': '11'
    }, scaleGroup);
  this.propeller_.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href',
    'media/propeller.svg');
  this.fedora_ = Blockly.utils.dom.createSvgElement('image',
    {
      'width': '50',
      'height': '12'
    }, scaleGroup);
  this.fedora_.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href',
    'media/fedora.svg');

  // Even if we're not going to display it right away, we want to create all
  // of our DOM elements inside this function.
  this.crown_.style.display = 'none';
  this.mask_.style.display = 'none';
  this.propeller_.style.display = 'none';
  this.fedora_.style.display = 'none';

  this.movableGroup_.appendChild(this.textElement_);
  this.textElement_.setAttribute(
      'transform', 'translate(' + this.TEXT_OFFSET_X + ',20)');

  this.defs_ = Blockly.utils.dom.createSvgElement('defs', {}, this.fieldGroup_);
  this.polkadotPattern_ = Blockly.utils.dom.createSvgElement('pattern',
    {
      'id': 'polkadots',
      'patternUnits': 'userSpaceOnUse',
      'width': 10,
      'height': 10
    }, this.defs_);
  this.polkadotGroup_ = Blockly.utils.dom.createSvgElement(
    'g', {}, this.polkadotPattern_);
  Blockly.utils.dom.createSvgElement('circle',
    {
      'cx': 2.5,
      'cy': 2.5,
      'r': 2.5,
      'fill': '#000',
      'fill-opacity': .3
    }, this.polkadotGroup_);
  Blockly.utils.dom.createSvgElement('circle',
    {
      'cx': 7.5,
      'cy': 7.5,
      'r': 2.5,
      'fill': '#000',
      'fill-opacity': .3
    }, this.polkadotGroup_);

  this.hexagonPattern_ = Blockly.utils.dom.createSvgElement('pattern',
    {
      'id': 'hexagons',
      'patternUnits': 'userSpaceOnUse',
      'width': 10,
      'height': 8.68,
      'patternTransform': 'translate(2) rotate(45)'
    }, this.defs_);
  Blockly.utils.dom.createSvgElement('polygon',
    {
      'id': 'hex',
      'points': '4.96,4.4 7.46,5.84 7.46,8.74 4.96,10.18 2.46,8.74 2.46,5.84',
      'stroke': '#000',
      'stroke-opacity': .3,
      'fill-opacity': 0
    }, this.hexagonPattern_);
  var use = Blockly.utils.dom.createSvgElement('use',
    {
      'x': 5,
    }, this.hexagonPattern_);
  use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#hex');
  use = Blockly.utils.dom.createSvgElement('use',
    {
      'x': -5,
    }, this.hexagonPattern_);
  use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#hex');
  use = Blockly.utils.dom.createSvgElement('use',
    {
      'x': 2.5,
      'y': -4.34
    }, this.hexagonPattern_);
  use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#hex');
  use = Blockly.utils.dom.createSvgElement('use',
    {
      'x': -2.5,
      'y': -4.34
    }, this.hexagonPattern_);
  use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#hex');

  this.stripesPattern_ = Blockly.utils.dom.createSvgElement('pattern',
    {
      'id': 'stripes',
      'patternUnits': 'userSpaceOnUse',
      'width': 5,
      'height': 10,
      'patternTransform': 'rotate(45)'
    }, this.defs_);
  Blockly.utils.dom.createSvgElement('line',
    {
      'x1': 0,
      'y1': 0,
      'x2': 0,
      'y2': 10,
      'stroke-width': 4,
      'stroke': '#000',
      'stroke-opacity': .3
    }, this.stripesPattern_);
};
