text: /**
   * @license
   * Visual Blocks Editor
   *
   * Copyright 2017 Google Inc.
   * https://developers.google.com/blockly/
   *
   * 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 Data Flyout components including variable and list blocks.
   * @fileoverview Flyout 中变量和块列表的数据
   * @author marisaleung@google.com (Marisa Leung)
   */
  'use strict';
  
  /**
   * @name Blockly.DataCategory
   * @namespace
   **/
  goog.provide('Blockly.DataCategory');
  
  goog.require('Blockly.Blocks');
  goog.require('Blockly.VariableModel');
  goog.require('Blockly.Variables');
  goog.require('Blockly.Workspace');
  
  /**
   * Construct the blocks required by the flyout for the variable category.
   * 为变量类别构造 flyout 所需的块。
   * @param {!Blockly.Workspace} workspace The workspace containing variables.
   * @return {!Array.<!Element>} Array of XML block elements.
   */
  Blockly.DataCategory = function(workspace) {
    var variableModelList = workspace.getVariablesOfType('');
    variableModelList.sort(Blockly.VariableModel.compareByName);
    var xmlList = [];
  
    Blockly.DataCategory.addCreateButton(xmlList, workspace, 'VARIABLE');
  
    for (var i = 0; i < variableModelList.length; i++) {
      Blockly.DataCategory.addDataVariable(xmlList, variableModelList[i]);
    }
  
    if (variableModelList.length > 0) {
      xmlList[xmlList.length - 1].setAttribute('gap', 24);
      var firstVariable = variableModelList[0];
  
      Blockly.DataCategory.addSetVariableTo(xmlList, firstVariable);
      Blockly.DataCategory.addChangeVariableBy(xmlList, firstVariable);
      Blockly.DataCategory.addShowVariable(xmlList, firstVariable);
      Blockly.DataCategory.addHideVariable(xmlList, firstVariable);
    }
  
    // Now add list variables to the flyout
    // 现在添加一个变量列表至 flyout
    Blockly.DataCategory.addCreateButton(xmlList, workspace, 'LIST');
    variableModelList = workspace.getVariablesOfType(Blockly.LIST_VARIABLE_TYPE);
    variableModelList.sort(Blockly.VariableModel.compareByName);
    for (var i = 0; i < variableModelList.length; i++) {
      Blockly.DataCategory.addDataList(xmlList, variableModelList[i]);
    }
  
    if (variableModelList.length > 0) {
      xmlList[xmlList.length - 1].setAttribute('gap', 24);
      var firstVariable = variableModelList[0];
  
      Blockly.DataCategory.addAddToList(xmlList, firstVariable);
      Blockly.DataCategory.addSep(xmlList);
      Blockly.DataCategory.addDeleteOfList(xmlList, firstVariable);
      Blockly.DataCategory.addDeleteAllOfList(xmlList, firstVariable);
      Blockly.DataCategory.addInsertAtList(xmlList, firstVariable);
      Blockly.DataCategory.addReplaceItemOfList(xmlList, firstVariable);
      Blockly.DataCategory.addSep(xmlList);
      Blockly.DataCategory.addItemOfList(xmlList, firstVariable);
      Blockly.DataCategory.addItemNumberOfList(xmlList, firstVariable);
      Blockly.DataCategory.addLengthOfList(xmlList, firstVariable);
      Blockly.DataCategory.addListContainsItem(xmlList, firstVariable);
      Blockly.DataCategory.addSep(xmlList);
      Blockly.DataCategory.addShowList(xmlList, firstVariable);
      Blockly.DataCategory.addHideList(xmlList, firstVariable);
    }
  
    return xmlList;
  };
  
  /**
   * Construct and add a data_variable block to xmlList.
   * @param {!Array.<!Element>} xmlList Array of XML block elements.
   * @param {?Blockly.VariableModel} variable Variable to select in the field.
   */
  Blockly.DataCategory.addDataVariable = function(xmlList, variable) {
    // <block id="variableId" type="data_variable">
    //    <field name="VARIABLE">variablename</field>
    // </block>
    Blockly.DataCategory.addBlock(xmlList, variable, 'data_variable', 'VARIABLE');
    // In the flyout, this ID must match variable ID for monitor syncing reasons
    // 在 flyout 中，由于监视器同步的原因，此 ID 必须要与变量 ID 相匹配。
    xmlList[xmlList.length - 1].setAttribute('id', variable.getId());
  };
  
  /**
   * Construct and add a data_setvariableto block to xmlList.
   * 构建并添加一个 data_setvariableto 块至 xmlList
   * @param {!Array.<!Element>} xmlList Array of XML block elements.
   * @param {?Blockly.VariableModel} variable Variable to select in the field.
   */
  Blockly.DataCategory.addSetVariableTo = function(xmlList, variable) {
    // <block type="data_setvariableto" gap="20">
    //   <value name="VARIABLE">
    //    <shadow type="data_variablemenu"></shadow>
    //   </value>
    //   <value name="VALUE">
    //     <shadow type="text">
    //       <field name="TEXT">0</field>
    //     </shadow>
    //   </value>
    // </block>
    Blockly.DataCategory.addBlock(xmlList, variable, 'data_setvariableto',
        'VARIABLE', ['VALUE', 'text', 0]);
  };
  
  /**
   * Construct and add a data_changevariableby block to xmlList.
   * @param {!Array.<!Element>} xmlList Array of XML block elements.
   * @param {?Blockly.VariableModel} variable Variable to select in the field.
   */
  Blockly.DataCategory.addChangeVariableBy = function(xmlList, variable) {
    // <block type="data_changevariableby">
    //   <value name="VARIABLE">
    //    <shadow type="data_variablemenu"></shadow>
    //   </value>
    //   <value name="VALUE">
    //     <shadow type="math_number">
    //       <field name="NUM">1</field>
    //     </shadow>
    //   </value>
    // </block>
    Blockly.DataCategory.addBlock(xmlList, variable, 'data_changevariableby',
        'VARIABLE', ['VALUE', 'math_number', 1]);
  };
  
  /**
   * Construct and add a data_showVariable block to xmlList.
   * @param {!Array.<!Element>} xmlList Array of XML block elements.
   * @param {?Blockly.VariableModel} variable Variable to select in the field.
   */
  Blockly.DataCategory.addShowVariable = function(xmlList, variable) {
    // <block type="data_showvariable">
    //   <value name="VARIABLE">
    //     <shadow type="data_variablemenu"></shadow>
    //   </value>
    // </block>
    Blockly.DataCategory.addBlock(xmlList, variable, 'data_showvariable',
        'VARIABLE');
  };
  
  /**
   * Construct and add a data_hideVariable block to xmlList.
   * @param {!Array.<!Element>} xmlList Array of XML block elements.
   * @param {?Blockly.VariableModel} variable Variable to select in the field.
   */
  Blockly.DataCategory.addHideVariable = function(xmlList, variable) {
    // <block type="data_hidevariable">
    //   <value name="VARIABLE">
    //     <shadow type="data_variablemenu"></shadow>
    //   </value>
    // </block>
    Blockly.DataCategory.addBlock(xmlList, variable, 'data_hidevariable',
        'VARIABLE');
  };
  
  /**
   * Construct and add a data_listcontents block to xmlList.
   * @param {!Array.<!Element>} xmlList Array of XML block elements.
   * @param {?Blockly.VariableModel} variable Variable to select in the field.
   */
  Blockly.DataCategory.addDataList = function(xmlList, variable) {
    // <block id="variableId" type="data_listcontents">
    //    <field name="LIST">variablename</field>
    // </block>
    Blockly.DataCategory.addBlock(xmlList, variable, 'data_listcontents', 'LIST');
    // In the flyout, this ID must match variable ID for monitor syncing reasons
    xmlList[xmlList.length - 1].setAttribute('id', variable.getId());
  };
  
  /**
   * Construct and add a data_addtolist block to xmlList.
   * @param {!Array.<!Element>} xmlList Array of XML block elements.
   * @param {?Blockly.VariableModel} variable Variable to select in the field.
   */
  Blockly.DataCategory.addAddToList = function(xmlList, variable) {
    // <block type="data_addtolist">
    //   <field name="LIST" variabletype="list" id="">variablename</field>
    //   <value name="ITEM">
    //     <shadow type="text">
    //       <field name="TEXT">thing</field>
    //     </shadow>
    //   </value>
    // </block>
    Blockly.DataCategory.addBlock(xmlList, variable, 'data_addtolist', 'LIST',
        ['ITEM', 'text', Blockly.Msg.DEFAULT_LIST_ITEM]);
  };
  
  /**
   * Construct and add a data_deleteoflist block to xmlList.
   * @param {!Array.<!Element>} xmlList Array of XML block elements.
   * @param {?Blockly.VariableModel} variable Variable to select in the field.
   */
  Blockly.DataCategory.addDeleteOfList = function(xmlList, variable) {
    // <block type="data_deleteoflist">
    //   <field name="LIST" variabletype="list" id="">variablename</field>
    //   <value name="INDEX">
    //     <shadow type="math_integer">
    //       <field name="NUM">1</field>
    //     </shadow>
    //   </value>
    // </block>
    Blockly.DataCategory.addBlock(xmlList, variable, 'data_deleteoflist', 'LIST',
        ['INDEX', 'math_integer', 1]);
  };
  
  /**
   * Construct and add a data_deleteoflist block to xmlList.
   * @param {!Array.<!Element>} xmlList Array of XML block elements.
   * @param {?Blockly.VariableModel} variable Variable to select in the field.
   */
  Blockly.DataCategory.addDeleteAllOfList = function(xmlList, variable) {
    // <block type="data_deletealloflist">
    //   <field name="LIST" variabletype="list" id="">variablename</field>
    // </block>
    Blockly.DataCategory.addBlock(xmlList, variable, 'data_deletealloflist',
        'LIST');
  };
  
  /**
   * Construct and add a data_insertatlist block to xmlList.
   * @param {!Array.<!Element>} xmlList Array of XML block elements.
   * @param {?Blockly.VariableModel} variable Variable to select in the field.
   */
  Blockly.DataCategory.addInsertAtList = function(xmlList, variable) {
    // <block type="data_insertatlist">
    //   <field name="LIST" variabletype="list" id="">variablename</field>
    //   <value name="INDEX">
    //     <shadow type="math_integer">
    //       <field name="NUM">1</field>
    //     </shadow>
    //   </value>
    //   <value name="ITEM">
    //     <shadow type="text">
    //       <field name="TEXT">thing</field>
    //     </shadow>
    //   </value>
    // </block>
    Blockly.DataCategory.addBlock(xmlList, variable, 'data_insertatlist', 'LIST',
        ['INDEX', 'math_integer', 1], ['ITEM', 'text', Blockly.Msg.DEFAULT_LIST_ITEM]);
  };
  
  /**
   * Construct and add a data_replaceitemoflist block to xmlList.
   * @param {!Array.<!Element>} xmlList Array of XML block elements.
   * @param {?Blockly.VariableModel} variable Variable to select in the field.
   */
  Blockly.DataCategory.addReplaceItemOfList = function(xmlList, variable) {
    // <block type="data_replaceitemoflist">
    //   <field name="LIST" variabletype="list" id="">variablename</field>
    //   <value name="INDEX">
    //     <shadow type="math_integer">
    //       <field name="NUM">1</field>
    //     </shadow>
    //   </value>
    //   <value name="ITEM">
    //     <shadow type="text">
    //       <field name="TEXT">thing</field>
    //     </shadow>
    //   </value>
    // </block>
    Blockly.DataCategory.addBlock(xmlList, variable, 'data_replaceitemoflist',
        'LIST', ['INDEX', 'math_integer', 1], ['ITEM', 'text', Blockly.Msg.DEFAULT_LIST_ITEM]);
  };
  
  /**
   * Construct and add a data_itemoflist block to xmlList.
   * @param {!Array.<!Element>} xmlList Array of XML block elements.
   * @param {?Blockly.VariableModel} variable Variable to select in the field.
   */
  Blockly.DataCategory.addItemOfList = function(xmlList, variable) {
    // <block type="data_itemoflist">
    //   <field name="LIST" variabletype="list" id="">variablename</field>
    //   <value name="INDEX">
    //     <shadow type="math_integer">
    //       <field name="NUM">1</field>
    //     </shadow>
    //   </value>
    // </block>
    Blockly.DataCategory.addBlock(xmlList, variable, 'data_itemoflist', 'LIST',
        ['INDEX', 'math_integer', 1]);
  };
  
  /** Construct and add a data_itemnumoflist block to xmlList.
   * @param {!Array.<!Element>} xmlList Array of XML block elements.
   * @param {?Blockly.VariableModel} variable Variable to select in the field.
   */
  Blockly.DataCategory.addItemNumberOfList = function(xmlList, variable) {
    // <block type="data_itemnumoflist">
    //   <value name="ITEM">
    //     <shadow type="text">
    //       <field name="TEXT">thing</field>
    //     </shadow>
    //   </value>
    //   <field name="LIST" variabletype="list" id="">variablename</field>
    // </block>
    Blockly.DataCategory.addBlock(xmlList, variable, 'data_itemnumoflist',
        'LIST', ['ITEM', 'text', Blockly.Msg.DEFAULT_LIST_ITEM]);
  };
  
  /**
   * Construct and add a data_lengthoflist block to xmlList.
   * @param {!Array.<!Element>} xmlList Array of XML block elements.
   * @param {?Blockly.VariableModel} variable Variable to select in the field.
   */
  Blockly.DataCategory.addLengthOfList = function(xmlList, variable) {
    // <block type="data_lengthoflist">
    //   <field name="LIST" variabletype="list" id="">variablename</field>
    // </block>
    Blockly.DataCategory.addBlock(xmlList, variable, 'data_lengthoflist', 'LIST');
  };
  
  /**
   * Construct and add a data_listcontainsitem block to xmlList.
   * @param {!Array.<!Element>} xmlList Array of XML block elements.
   * @param {?Blockly.VariableModel} variable Variable to select in the field.
   */
  Blockly.DataCategory.addListContainsItem = function(xmlList, variable) {
    // <block type="data_listcontainsitem">
    //   <field name="LIST" variabletype="list" id="">variablename</field>
    //   <value name="ITEM">
    //     <shadow type="text">
    //       <field name="TEXT">thing</field>
    //     </shadow>
    //   </value>
    // </block>
    Blockly.DataCategory.addBlock(xmlList, variable, 'data_listcontainsitem',
        'LIST', ['ITEM', 'text', Blockly.Msg.DEFAULT_LIST_ITEM]);
  };
  
  /**
   * Construct and add a data_showlist block to xmlList.
   * @param {!Array.<!Element>} xmlList Array of XML block elements.
   * @param {?Blockly.VariableModel} variable Variable to select in the field.
   */
  Blockly.DataCategory.addShowList = function(xmlList, variable) {
    // <block type="data_showlist">
    //   <field name="LIST" variabletype="list" id="">variablename</field>
    // </block>
    Blockly.DataCategory.addBlock(xmlList, variable, 'data_showlist', 'LIST');
  };
  
  /**
   * Construct and add a data_hidelist block to xmlList.
   * @param {!Array.<!Element>} xmlList Array of XML block elements.
   * @param {?Blockly.VariableModel} variable Variable to select in the field.
   */
  Blockly.DataCategory.addHideList = function(xmlList, variable) {
    // <block type="data_hidelist">
    //   <field name="LIST" variabletype="list" id="">variablename</field>
    // </block>
    Blockly.DataCategory.addBlock(xmlList, variable, 'data_hidelist', 'LIST');
  };
  
  /**
   * Construct a create variable button and push it to the xmlList.
   * @param {!Array.<!Element>} xmlList Array of XML block elements.
   * @param {Blockly.Workspace} workspace Workspace to register callback to.
   * @param {string} type Type of variable this is for. For example, 'LIST' or
   *     'VARIABLE'.
   */
  Blockly.DataCategory.addCreateButton = function(xmlList, workspace, type) {
    var button = goog.dom.createDom('button');
    // Set default msg, callbackKey, and callback values for type 'VARIABLE'
    var msg = Blockly.Msg.NEW_VARIABLE;
    var callbackKey = 'CREATE_VARIABLE';
    var callback = function(button) {
      Blockly.Variables.createVariable(button.getTargetWorkspace(), null, '');};
  
    if (type === 'LIST') {
      msg = Blockly.Msg.NEW_LIST;
      callbackKey = 'CREATE_LIST';
      callback = function(button) {
        Blockly.Variables.createVariable(button.getTargetWorkspace(), null,
            Blockly.LIST_VARIABLE_TYPE);};
    }
    button.setAttribute('text', msg);
    button.setAttribute('callbackKey', callbackKey);
    workspace.registerButtonCallback(callbackKey, callback);
    xmlList.push(button);
  };
  
  /**
   * Construct a variable block with the given variable, blockType, and optional
   *     value tags. Add the variable block to the given xmlList.
   * 使用给定的 variable、blockType 和可选值标签构建一个变量块。添加变量块至给定的 xmlList。
   * @param {!Array.<!Element>} xmlList Array of XML block elements.
   * @param {?Blockly.VariableModel} variable Variable to select in the field.
   * @param {string} blockType Type of block. For example, 'data_hidelist' or
   *     data_showlist'.
   * @param {string} fieldName Name of field in block. For example: 'VARIABLE' or
   *     'LIST'.
   * @param {?Array.<string>} opt_value Optional array containing the value name
   *     and shadow type of value tags.
   * @param {?Array.<string>} opt_secondValue Optional array containing the value
   *     name and shadow type of a second pair of value tags.
   */
  Blockly.DataCategory.addBlock = function(xmlList, variable, blockType,
      fieldName, opt_value, opt_secondValue) {
    if (Blockly.Blocks[blockType]) {
      var firstValueField;
      var secondValueField;
      if (opt_value) {
        firstValueField = Blockly.DataCategory.createValue(opt_value[0],
            opt_value[1], opt_value[2]);
      }
      if (opt_secondValue) {
        secondValueField = Blockly.DataCategory.createValue(opt_secondValue[0],
            opt_secondValue[1], opt_secondValue[2]);
      }
  
      var gap = 8;
      var blockText = '<xml>' +
          '<block type="' + blockType + '" gap="' + gap + '">' +
          Blockly.Variables.generateVariableFieldXml_(variable, fieldName) +
          firstValueField + secondValueField +
          '</block>' +
          '</xml>';
      var block = Blockly.Xml.textToDom(blockText).firstChild;
      xmlList.push(block);
    }
  };
  
  /**
   * Create the text representation of a value dom element with a shadow of the
   *     indicated type inside.
   * @param {string} valueName Name of the value tags.
   * @param {string} type The type of the shadow tags.
   * @param {string|number} value The default shadow value.
   * @return {string} The generated dom element in text.
   */
  Blockly.DataCategory.createValue = function(valueName, type, value) {
    var fieldName;
    switch (valueName) {
      case 'ITEM':
        fieldName = 'TEXT';
        break;
      case 'INDEX':
        fieldName = 'NUM';
        break;
      case 'VALUE':
        if (type === 'math_number') {
          fieldName = 'NUM';
        } else {
          fieldName = 'TEXT';
        }
        break;
    }
    var valueField =
        '<value name="' + valueName + '">' +
        '<shadow type="' + type + '">' +
        '<field name="' + fieldName + '">' + value + '</field>' +
        '</shadow>' +
        '</value>';
    return valueField;
  };
  
  /**
   * Construct a block separator. Add the separator to the given xmlList.
   * @param {!Array.<!Element>} xmlList Array of XML block elements.
   */
  Blockly.DataCategory.addSep = function(xmlList) {
    var gap = 36;
    var sepText = '<xml>' +
        '<sep gap="' + gap + '"/>' +
        '</xml>';
    var sep = Blockly.Xml.textToDom(sepText).firstChild;
    xmlList.push(sep);
  };
  