text: /**
   * @license
   * Visual Blocks Editor
   *
   * Copyright 2013 Google Inc.
   * https://blockly.googlecode.com/
   *
   * 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 Flexible templating system for defining blocks.
   * @author spertus@google.com (Ellen Spertus)
   */
  'use strict';
  goog.require('goog.asserts');
  goog.require('Blockly.Warning');
  goog.require('goog.events.BrowserFeature');
  goog.require('goog.html.SafeHtml');
  goog.require('goog.style');
  goog.require('goog.ui.tree.TreeControl');
  goog.require('goog.ui.tree.TreeNode');
  goog.require('goog.Disposable');
  
  /**
   * Name space for the Blocks singleton.
   * Blocks gets populated in the blocks files, possibly through calls to
   * Blocks.addTemplate().
   */
  goog.provide('Blockly.Blocks');
  /**
   * Create a block template and add it as a field to Blockly.Blocks with the
   * name details.blockName.
   * @param {!Object} details Details about the block that should be created.
   *     The following fields are used:
   *     - blockName {string} The name of the block, which should be unique.
   *     - colour {number} The hue value of the colour to use for the block.
   *       (Blockly.HSV_SATURATION and Blockly.HSV_VALUE are used for saturation
   *       and value, respectively.)
   *     - output {?string|Array.<string>} Output type.  If undefined, there are
   *       assumed to be no outputs.  Otherwise, this is interpreted the same way
   *       as arguments to Blockly.Block.setCheck():
   *       - null: Any type can be produced.
   *       - String: Only the specified type (e.g., 'Number') can be produced.
   *       - Array.<string>: Any of the specified types can be produced.
   *     - message {string} A message suitable for passing as a first argument to
   *       Blockly.Block.interpolateMsg().  Specifically, it should consist of
   *       text to be displayed on the block, optionally interspersed with
   *       references to inputs (one-based indices into the args array) or fields,
   *       such as '%1' for the first element of args.  The creation of dummy
   *       inputs can be forced with a newline (\n).
   *     - args {Array.<Object>} One or more descriptions of value inputs.
   *       TODO: Add Fields and statement stacks.
   *       Each object in the array can have the following fields:
   *       - name {string} The name of the input.
   *       - type {?number} One of Blockly.INPUT_VALUE, Blockly.NEXT_STATEMENT, or
   *         ??.   If not provided, it is assumed to be Blockly.INPUT_VALUE.
   *       - check {?string|Array.<string>} Input type.  See description of the
   *         output field above.
   *       - align {?number} One of Blockly.ALIGN_LEFT, Blockly.ALIGN_CENTRE, or
   *         Blockly.ALIGN_RIGHT (the default value, if not explicitly provided).
   *     - inline {?boolean}: Whether inputs should be inline (true) or external
   *       (false).  If not explicitly specified, inputs will be inline if message
   *       references, and ends with, a single value input.
   *     - previousStatement {?boolean} Whether there should be a statement
   *       connector on the top of the block.  If not specified, the default
   *       value will be !output.
   *     - nextStatement {?boolean} Whether there should be a statement
   *       connector on the bottom of the block.  If not specified, the default
   *       value will be !output.
   *     - tooltip {?string|Function} Tooltip text or a function on this block
   *       that returns a tooltip string.
   *     - helpUrl {?string|Function} The help URL, or a function on this block
   *       that returns the help URL.
   *     - switchable {?boolean} Whether the block should be switchable between
   *       an expression and statement.  Specifically, if true, the block will
   *       begin as an expression (having an output).  There will be a context
   *       menu option 'Remove output'.  If selected, the output will disappear,
   *       and previous and next statement connectors will appear.  The context
   *       menu option 'Remove output' will be replaced by 'Add Output'.  If
   *       selected, the output will reappear and the statement connectors will
   *       disappear.
   *     - mutationToDomFunc {Function} TODO desc.
   *     - domToMutationFunc {Function} TODO desc.
   *     - customContextMenuFunc {Function} TODO desc.
   *     Additional fields will be ignored.
   */
  Blockly.Blocks.addTemplate = function(details) {
      // Validate inputs.  TODO: Add more.
      goog.asserts.assert(details.blockName);
      goog.asserts.assert(Blockly.Blocks[details.blockName],
          'Blockly.Blocks already has a field named ', details.blockName);
      goog.asserts.assert(details.message);
      goog.asserts.assert(details.colour && typeof details.colour == 'number' &&
          details.colour >= 0 && details.colour < 360,
          'details.colour must be a number from 0 to 360 (exclusive)');
      if (details.output != 'undefined') {
          goog.asserts.assert(!details.previousStatement,
              'When details.output is defined, ' +
              'details.previousStatement must not be true.');
          goog.asserts.assert(!details.nextStatement,
              'When details.output is defined, ' +
              'details.nextStatement must not be true.');
      }
  
      // Build up template.
      var block = {};
      block.init = function() {
          var thisBlock = this;
          // Set basic properties of block.
          this.setColour(details.colour);
          this.setHelpUrl(details.helpUrl);
          if (typeof details.tooltip == 'string') {
              this.setTooltip(details.tooltip);
          } else if (typeof details.tooltip == 'function') {
              this.setTooltip(function() {
                  return details.tooltip(thisBlock);
              });
          }
          // Set output and previous/next connections.
          if (details.output != 'undefined') {
              this.setOutput(true, details.output);
          } else {
              this.setPreviousStatement(
                  typeof details.previousStatement == 'undefined' ?
                      true : details.previousStatement);
              this.setNextStatement(
                  typeof details.nextStatement == 'undefined' ?
                      true : details.nextStatement);
          }
          // Build up arguments in the format expected by interpolateMsg.
          var interpArgs = [];
          interpArgs.push(details.text);
          if (details.args) {
              details.args.forEach(function(arg) {
                  goog.asserts.assert(arg.name);
                  goog.asserts.assert(arg.check != 'undefined');
                  if (arg.type == 'undefined' || arg.type == Blockly.INPUT_VALUE) {
                      interpArgs.push([arg.name,
                          arg.check,
                          typeof arg.align == 'undefined' ? Blockly.ALIGN_RIGHT : arg.align
                      ]);
                  } else {
                      // TODO: Write code for other input types.
                      goog.asserts.fail('addTemplate() can only handle value inputs.');
                  }
              });
          }
          // Neil, how would you recommend specifying the final dummy alignment?
          // Should it be a top-level field in details?
          interpArgs.push(Blockly.ALIGN_RIGHT);
          if (details.inline) {
              this.setInlineInputs(details.inline);
          }
          Blockly.Block.prototype.interpolateMsg.apply(this, interpArgs);
  
          this.event = (details.event != 'undefined' ? details.event : 'step_line');
      };
  
      // Create mutationToDom if needed.
      if (details.switchable) {
          block.mutationToDom = function() {
              var container = details.mutationToDomFunc ? details.mutatationToDomFunc() : document.createElement('mutation');
              container.setAttribute('is_statement', this['isStatement'] || false);
              return container;
          };
      } else {
          block.mutationToDom = details.mutationToDomFunc;
      }
      // TODO: Add domToMutation and customContextMenu.
  
      // Add new block to Blockly.Blocks.
      Blockly.Blocks[details.blockName] = block;
  };
  
  /*
   The Function to set warning text and show it when the block
   that must be in function is out of function.
   */
  Blockly.Blocks.requireInFunction = function(block) {
      if(!block) {
          if (!this.workspace) {
              // Block has been deleted.
              return;
          }
          if (this.getSurroundParent()) {
              this.setWarningText(null);
          } else {
              this.setWarningText(Blockly.Msg.PLZ_INSIDE_FUNCTION);
          }
      }
      else {
          if (!block.workspace) {
              // Block has been deleted.
              return;
          }
          if (block.getSurroundParent()) {
              block.setWarningText(null);
          } else {
              block.setWarningText(Blockly.Msg.PLZ_INSIDE_FUNCTION);
          }
      }
  };
  /*
  Check if the statement block is used in a for-loop initialization or increment.
  */
  Blockly.Blocks.forPlacementCheck = function(block) {
      if (!block) {
          block = this;
      }
      if (!block.workspace) {
          // Block has been deleted.
          return;
      }
      if (block.getSurroundParent()
            && block.getSurroundParent().type == "controls_for"
            && [block.getSurroundParent().getInputTargetBlock("INIT"),
                block.getSurroundParent().getInputTargetBlock("INC")].indexOf(block) >= 0) {
  
          if (block.getNextBlock()) {
              block.getNextBlock().unplug(false, true);
          }
          block.setNextStatement(false);
      } else if (!block.nextConnection) {
          block.setNextStatement(true, block.previousConnection.check_);
      }
  };
  /*
   The Function to check if variable, array, #define, or pointer declare block's position is legal or illegal.
   */
  Blockly.Blocks.variablePlaceCheck = function(block) {
      if(!block) {
          if (!this.workspace) {
              // Block has been deleted.
              return;
          }
          if (this.getSurroundParent() && (this.getSurroundParent().type == 'main_block' || this.getSurroundParent().type == 'procedures_defnoreturn' || this.getSurroundParent().type == 'procedures_defreturn')) {
              this.setWarningText(null);
          } else if (this.getSurroundParent()) {
              this.setWarningText(Blockly.Msg.PLZ_OUT_OF_BLOCK);
          } else {
              this.setWarningText(Blockly.Msg.PLZ_INSIDE_FUNCTION);
          }
      }
      else {
          if (!block.workspace) {
              // Block has been deleted.
              return;
          }
          if (block.getSurroundParent() && (block.getSurroundParent().type == 'main_block' || block.getSurroundParent().type == 'procedures_defnoreturn' || block.getSurroundParent().type == 'procedures_defreturn')) {
              block.setWarningText(null);
          } else if (block.getSurroundParent()) {
              block.setWarningText(Blockly.Msg.PLZ_OUT_OF_BLOCK);
          } else {
              block.setWarningText(Blockly.Msg.PLZ_INSIDE_FUNCTION);
          }
      }
  };
  
  Blockly.Blocks.requireOutFunction=function(block){
      if(!block) {
          if (!this.workspace) {
              // Block has been deleted.
              return;
          }
          if (this.getSurroundParent() && (this.getSurroundParent().type == 'main_block' || this.getSurroundParent().type == 'procedures_defnoreturn' || this.getSurroundParent().type == 'procedures_defreturn')) {
              this.setWarningText(Blockly.Msg.PLZ_OUT_OF_FUNCTION);
          } else {
              this.setWarningText(null);
          }
      }
      else {
          if (!block.workspace) {
              // Block has been deleted.
              return;
          }
          if (block.getSurroundParent() && (block.getSurroundParent().type == 'main_block' || block.getSurroundParent().type == 'procedures_defnoreturn' || block.getSurroundParent().type == 'procedures_defreturn')) {
              block.setWarningText(Blockly.Msg.PLZ_OUT_OF_FUNCTION);
          } else {
              block.setWarningText(null);
          }
      }
  };
  
  Blockly.Blocks.checkArrayIndex = function(inputNum, arrayIdx) {
      // if inputNum is variable
      if (isNaN(inputNum) == true ){
          return true;
      }
      if ((inputNum < 0) || (arrayIdx < 0) || (inputNum >= arrayIdx)) {
          return false;
      }
      else
          return true;
  };
  
  Blockly.Blocks.getWantedBlockArray = function(wantedType) {
      var varList = Blockly.Variables.allVariables();
      var wantedList = [];
      for (var temp = 0 ; temp < varList.length ; temp++ ){
          if (varList[temp][1] == wantedType) {
              wantedList.push([varList[temp][0], varList[temp][1], varList[temp][2], varList[temp][3], varList[temp][4], varList[temp][5]]);
          }
      }
  
      return wantedList;
  };
  
  Blockly.Blocks.getIndexArray = function(arrList, arrName) {
      var idxList = [];
      var fixedIdx1, fixedIdx2, fixedIdx3;
      for (var temp = 0 ; temp < arrList.length ; temp++) {
          if (arrList[temp][2] == arrName) {
              fixedIdx1 = arrList[temp][5][1];
              fixedIdx2 = arrList[temp][5][2];
              fixedIdx3 = arrList[temp][5][3];
  
              switch(arrList[temp][5][0]) {
  
                  case 1:
                      fixedIdx2 = -1;
                      fixedIdx3 = -1;
                      break;
                  case 2:
                      fixedIdx3 = -1;
                      break;
                  default:
                      break;
              }
  
          }
      }
      idxList.push(fixedIdx1, fixedIdx2, fixedIdx3);
      return idxList;
  };
  
  
  Blockly.Blocks.arrayTestFunction = function(block, len1, len2, len3){
  
      if(len1 != 0 && len2 == 0 && len3 == 0)
          block.setWarningText(null);
      else if(len1 != 0 && len2 != 0 && len3 == 0)
          block.setWarningText(null);
      else if(len1 != 0 && len2 != 0 && len3 != 0)
          block.setWarningText(null);
      else
          block.setWarningText('Warning: Array length must be writen by order.');
  
  };
  
  /**
   * block search and show the result.
   * just use searchTag function and showResult function.
   * @param searchingWord
   */
  Blockly.Blocks.search = function(searchingWord){
      var result = Blockly.Blocks.searchTag(searchingWord);
      Blockly.Blocks.showResult(result);
  };
  
  /**
   * searching tag from all blocks
   * return block array that have the tag
   * @param searchingTag
   * @returns {Array}
   */
  Blockly.Blocks.searchTag = function(searchingTag){
      var tree = Blockly.Toolbox.tree_;
      var blocks = [];
      for (var i = 0; i<tree.children_.length; i++) {
          var tree_i =tree.children_[i];
          if(tree_i.blocks == 'PROCEDURE'){
              var proNoReturn = new Blockly.Block();
              proNoReturn.id = Blockly.genUid();
              proNoReturn.fill(Blockly.mainWorkspace, "procedures_defnoreturn");
              blocks.push(proNoReturn);
  
              var proReturn = new Blockly.Block();
              proReturn.id = Blockly.genUid();
              proReturn.fill(Blockly.mainWorkspace, "procedures_defreturn");
              blocks.push(proReturn);
          }
          else if(tree_i.blocks =='STRUCTURE'){
              var structDefine = new Blockly.Block();
              structDefine.id = Blockly.genUid();
              structDefine.fill(Blockly.mainWorkspace, "structure_define");
              blocks.push(structDefine);
  
              var structDeclare = new Blockly.Block();
              structDeclare.id = Blockly.genUid();
              structDeclare.fill(Blockly.mainWorkspace, "structure_declare");
              blocks.push(structDeclare);
          }
          else if(tree_i.blocks.length){
              for(var j =0;j<tree_i.blocks.length;j++){
                  var block = Blockly.Xml.domToBlockObject(Blockly.mainWorkspace, tree_i.blocks[j]);
                  blocks.push(block);
              }
          }
          else if(tree_i.html_.privateDoNotAccessOrElseSafeHtmlWrappedValue_ != 'result' && tree_i.children_.length){
              for(var j=0;j<tree_i.children_.length;j++){
                  var tree_j=tree_i.children_[j];
                  if(tree_j.blocks){
                      for(var k=0;k<tree_j.blocks.length;k++){
                          var block = Blockly.Xml.domToBlockObject(Blockly.mainWorkspace, tree_j.blocks[k]);
                          blocks.push(block);
                      }
                  }
              }
          }
      }
  
      var result = [];
      for(var n=0;n<blocks.length;n++){
          if(blocks[n].tag){
              for(var m=0;m<blocks[n].tag.length;m++){
                  if(blocks[n].tag[m].indexOf(searchingTag) != -1){
                      result.push(blocks[n]);
                      break;
                  }
              }
          }
      }
      return result;
  };
  
  /**
   * Check if type is same as one of result or not.
   * If there is same type block among result, function return the index.
   * If there is no same type block among result, function return -1 value.
   * @param type: type that will be checked.
   * @param result: list of blocks.
   * @returns {number}
   */
  Blockly.Blocks.checkResult = function(type, result){
      var returnValue = -1;
      for(var i=0;i<result.length;i++){
          if(result[i].type.toUpperCase() == type){
              returnValue = i;
          }
      }
      return returnValue;
  };
  
  /**
   * rendering the block into main workspace to show the result to user
   * @param result
   */
  Blockly.Blocks.showResult = function(result){
  
      var tree = new Blockly.Toolbox.TreeControl(goog.html.SafeHtml.EMPTY,
          Blockly.Toolbox.CONFIG_);
      Blockly.Toolbox.tree_ = tree;
      tree.setShowRootNode(false);
      tree.setShowLines(false);
      tree.setShowExpandIcons(false);
  
  
      var rootOut = Blockly.Toolbox.tree_;
      rootOut.removeChildren();  // Delete any existing content.
      rootOut.blocks = [];
      var searchResult = rootOut.createNode("result");
      searchResult.blocks = [];
      Blockly.Toolbox.tree_.add(searchResult);
      function syncTrees(treeIn, treeOut) {
          for (var i = 0, childIn; childIn = treeIn.childNodes[i]; i++) {
              if (!childIn.tagName) {
                  // Skip over text.
                  continue;
              }
              var name = childIn.tagName.toUpperCase();
              if (name == 'CATEGORY') {
                  var childOut = rootOut.createNode(childIn.getAttribute('name'));
                  childOut.blocks = [];
                  treeOut.add(childOut);
                  var custom = childIn.getAttribute('custom');
                  if (custom) {
                      childOut.blocks = custom;
                      for(var j = 0, child; child=childIn.childNodes[j];j++){
                          if(!child.tagName){
                              continue;
                          }
                          var childName = child.tagName.toUpperCase();
                          if(childName == 'BLOCK'){
                              var check = Blockly.Blocks.checkResult(child.getAttribute('type').toUpperCase(), result);
                              if(check != -1){
                                  result.splice(check, 1);
                                  searchResult.blocks.push(child);
                              }
                          }
                      }
                  } else {
                      syncTrees(childIn, childOut);
                  }
              } else if (name == 'BLOCK') {
                  treeOut.blocks.push(childIn);
                  var check = Blockly.Blocks.checkResult(childIn.getAttribute('type').toUpperCase(), result);
                  if(check != -1){
                      result.splice(check, 1);
                      searchResult.blocks.push(childIn);
                  }
              }
          }
      }
      syncTrees(Blockly.languageTree, Blockly.Toolbox.tree_);
  
      if (rootOut.blocks.length) {
          throw 'Toolbox cannot have both blocks and categories in the root level.';
      }
  
      // Fire a resize event since the toolbox may have changed width and height.
      Blockly.fireUiEvent(window, 'resize');
      Blockly.Toolbox.HtmlDiv.childNodes[0].remove();
      tree.setSelectedItem(searchResult);
      tree.render(Blockly.Toolbox.HtmlDiv);
  };
  
  Blockly.Blocks.checkLegalName = function(msg, name){
      var err = 0;
  
      if(name.length>0){
          var chk = name.substring(0,1);
          if(!chk.match(/[a-z]|[A-Z]/)){
              err = err+1;
          }
      }
      for (var i=1; i<name.length; i++)  {
          var chk = name.substring(i,i+1);
          if(!chk.match(/[0-9]|[a-z]|[A-Z]|_/)) {
              err = err + 1;
          }
      }
  
      if(err>0){
          window.alert(msg);
          return -1;
      }
      return;
  };
  
  
  /**
   * setCheck - variable with type
   * @param block
   * @param varType
   * @param inputName
   */
  Blockly.Blocks.setCheckVariable = function(block, varType, inputName) {
      switch (varType)
      {
          case('int'):
              block.getInput(inputName)
                  .setCheck(['Number', 'INTEGER', 'Aster', 'Array', 'Boolean', 'Macro', 'Variable', 'VAR_INT', 'VAR_CHAR', 'NEGATIVE', 'INT']);
              break;
          case('unsigned int'):
              block.getInput(inputName)
                  .setCheck(['Number', 'Aster', 'Array', 'Boolean', 'Macro', 'Variable', 'VAR_UNINT', 'NEGATIVE']);
              break;
          case('float') :
              block.getInput(inputName)
                  .setCheck(['Number', 'Aster', 'Array', 'Boolean', 'Macro', 'Variable', 'VAR_FLOAT', 'DOUBLE']);
              break;
          case('double') :
              block.getInput(inputName)
                  .setCheck(['Number', 'Aster', 'Array', 'Boolean', 'Macro', 'Variable', 'VAR_DOUBLE', 'DOUBLE']);
              break;
          case('char'):
              block.getInput(inputName)
                  .setCheck(['String', 'Aster', 'Array', 'Boolean', 'Macro', 'Variable', 'VAR_CHAR', 'CHAR', 'Number', 'INT']);
              break;
          case('std::string'):
              block.getInput(inputName)
                  .setCheck(['Variable', 'Array', 'Aster', 'Macro', 'VAR_STR', 'String']);
              break;
          /*        default:
           block.getInput(inputName)
           .setCheck(['Number', 'Aster', 'Array', 'Boolean', 'Macro', 'Variable', 'NEGATIVE', 'INT']);
           */
      }
  };
  
  /**
   * setCheck - pointer with type
   * @param block
   * @param ptrType
   * @param inputName
   */
  Blockly.Blocks.setCheckPointer = function(block, ptrType, inputName) {
      switch (ptrType) {
          case ('int'):
              block.getInput(inputName).setCheck(['PTR_INT', 'Address', 'Pointer', 'Array', 'Aster']);
              break;
          case ('unsigned int'):
              block.getInput(inputName).setCheck(['PTR_UNINT', 'Address', 'Pointer', 'Array', 'Aster']);
              break;
          case ('float'):
              block.getInput(inputName).setCheck(['PTR_FLOAT', 'Address', 'Pointer', 'Array', 'Aster']);
              break;
          case ('double'):
              block.getInput(inputName).setCheck(['PTR_DOUBLE', 'Address', 'Pointer', 'Array', 'Aster']);
              break;
          case ('char'):
              block.getInput(inputName).setCheck(['PTR_CHAR', 'Address', 'Pointer', 'String', 'STR', 'CHAR', 'Array', 'Aster']);
              break;
          case ('string'):
              block.getInput(inputName).setCheck(['PTR_STR', 'Address', 'Pointer', 'String', 'STR', 'CHAR', 'Array', 'Aster']);
              break;
          case ('dbint'):
              block.getInput(inputName).setCheck(['DBPTR_INT', 'Address', 'Pointer', 'Aster', 'Array', 'Aster']);
              break;
          case ('dbunsigned int'):
              block.getInput(inputName).setCheck(['DBPTR_UNINT', 'Address', 'Pointer', 'Aster', 'Array', 'Aster']);
              break;
          case ('dbfloat'):
              block.getInput(inputName).setCheck(['DBPTR_FLOAT', 'Address', 'Pointer', 'Aster', 'Array', 'Aster']);
              break;
          case ('dbdouble'):
              block.getInput(inputName).setCheck(['DBPTR_DOUBLE', 'Address', 'Pointer', 'Aster', 'Array', 'Aster']);
              break;
          case ('dbchar'):
              block.getInput(inputName).setCheck(['DBPTR_CHAR', 'Address', 'Pointer', 'String', 'STR', 'CHAR', 'Array', 'Aster']);
              break;
          case ('dbstring'):
              block.getInput(inputName).setCheck(['DBPTR_STR', 'Address', 'Pointer', 'String', 'STR', 'CHAR', 'Array', 'Aster']);
              break;
          /*        default:
           block.getInput(inputName).setCheck(['String', 'Pointer', 'Array', 'Aster']);
           */    }
  };
  
  Blockly.Blocks.checkUnselect = function(content){
      if(content == '___EC_84_A0_ED_83_9D__' || content == '--Select--' || content == '___ED_83_80_EC_9E_85__' || content == '--Type--'){
          content = 'unselected';
      }
      return content;
  }