<commit_before>/**
 * @license
 * Visual Blocks Editor
 *
 * Copyright 2012 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 Utility functions for handling variables.
 * @author fraser@google.com (Neil Fraser)
 */
'use strict';

/**
 * @name Blockly.Variables
 * @namespace
 **/
goog.provide('Blockly.Variables');

goog.require('Blockly.Blocks');
goog.require('Blockly.constants');
goog.require('Blockly.VariableModel');
goog.require('Blockly.Workspace');
goog.require('goog.string');

/**
 * Constant to separate variable names from procedures and generated functions
 * when running generators.
 * @deprecated Use Blockly.VARIABLE_CATEGORY_NAME
 */
Blockly.Variables.NAME_TYPE = Blockly.VARIABLE_CATEGORY_NAME;

/**
 * Find all user-created variables that are in use in the workspace.
 * For use by generators.
 * @param {!Blockly.Block|!Blockly.Workspace} root Root block or workspace.
 * @return {!Array.<string>} Array of variable names.
 */
Blockly.Variables.allUsedVariables = function(root) {
  var blocks;
  if (root instanceof Blockly.Block) {
    // Root is Block.
    blocks = root.getDescendants();
  } else if (root instanceof Blockly.Workspace ||
      root instanceof Blockly.WorkspaceSvg) {
    // Root is Workspace.
    blocks = root.getAllBlocks();
  } else {
    throw 'Not Block or Workspace: ' + root;
  }

  var ignorableName = Blockly.Variables.noVariableText();

  var variableHash = Object.create(null);
  // Iterate through every block and add each variable to the hash.
  for (var x = 0; x < blocks.length; x++) {
    var blockVariables = blocks[x].getVars();
    if (blockVariables) {
      for (var y = 0; y < blockVariables.length; y++) {
        var varName = blockVariables[y];
        // Variable name may be null if the block is only half-built.
        if (varName && varName.toLowerCase() != ignorableName) {
          variableHash[varName.toLowerCase()] = varName;
        }
      }
    }
  }
  // Flatten the hash into a list.
  var variableList = [];
  for (var name in variableHash) {
    variableList.push(variableHash[name]);
  }
  return variableList;
};

/**
 * Find all variables that the user has created through the workspace or
 * toolbox.  For use by generators.
 * @param {!Blockly.Workspace} root The workspace to inspect.
 * @return {!Array.<Blockly.VariableModel>} Array of variable models.
 */
Blockly.Variables.allVariables = function(root) {
  if (root instanceof Blockly.Block) {
    // Root is Block.
    console.warn('Deprecated call to Blockly.Variables.allVariables ' +
                 'with a block instead of a workspace.  You may want ' +
                 'Blockly.Variables.allUsedVariables');
    return {};
  }
  return root.getAllVariables();
};

/**
 * Construct the blocks required by the flyout for the variable category.
 * @param {!Blockly.Workspace} workspace The workspace contianing variables.
 * @return {!Array.<!Element>} Array of XML block elements.
 */
Blockly.Variables.flyoutCategory = function(workspace) {
  var variableModelList = workspace.getVariablesOfType('');
  variableModelList.sort(Blockly.VariableModel.compareByName);

  var xmlList = [];
  var button = goog.dom.createDom('button');
  button.setAttribute('text', Blockly.Msg.NEW_VARIABLE);
  button.setAttribute('callbackKey', 'CREATE_VARIABLE');

  workspace.registerButtonCallback('CREATE_VARIABLE', function(button) {
    Blockly.Variables.createVariable(button.getTargetWorkspace());
  });

  xmlList.push(button);

  for (var i = 0; i < variableModelList.length; i++) {
    if (Blockly.Blocks['data_variable']) {
      // <block type="data_variable">
      //    <field name="VARIABLE" variableType="" id="">variablename</field>
      // </block>
      var block = goog.dom.createDom('block');
      block.setAttribute('type', 'data_variable');
      block.setAttribute('gap', 8);

      var field = goog.dom.createDom('field', null, variableModelList[i].name);
      field.setAttribute('name', 'VARIABLE');
      field.setAttribute('variableType', variableModelList[i].type);
      field.setAttribute('id', variableModelList[i].getId());
      block.appendChild(field);

      xmlList.push(block);
    }
  }

  if (xmlList.length > 1) { // The button is always there.
    xmlList[xmlList.length - 1].setAttribute('gap', 24);

    if (Blockly.Blocks['data_setvariableto']) {
      // <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>
      var block = goog.dom.createDom('block');
      block.setAttribute('type', 'data_setvariableto');
      block.setAttribute('gap', 8);
      block.appendChild(Blockly.Variables.createVariableDom_(variableModelList[0]));
      block.appendChild(Blockly.Variables.createTextDom_());
      xmlList.push(block);
    }
    if (Blockly.Blocks['data_changevariableby']) {
      // <block type="data_changevariableby">
      //   <value name="VARIABLE">
      //    <shadow type="data_variablemenu"></shadow>
      //   </value>
      //   <value name="VALUE">
      //     <shadow type="math_number">
      //       <field name="NUM">0</field>
      //     </shadow>
      //   </value>
      // </block>
      var block = goog.dom.createDom('block');
      block.setAttribute('type', 'data_changevariableby');
      block.setAttribute('gap', 8);
      block.appendChild(Blockly.Variables.createVariableDom_(variableModelList[0]));
      block.appendChild(Blockly.Variables.createMathNumberDom_());
      xmlList.push(block);
    }
    if (Blockly.Blocks['data_showvariable']) {
      // <block type="data_showvariable">
      //   <value name="VARIABLE">
      //     <shadow type="data_variablemenu"></shadow>
      //   </value>
      // </block>
      var block = goog.dom.createDom('block');
      block.setAttribute('type', 'data_showvariable');
      block.setAttribute('gap', 8);
      block.appendChild(Blockly.Variables.createVariableDom_(variableModelList[0]));
      xmlList.push(block);
    }
    if (Blockly.Blocks['data_hidevariable']) {
      // <block type="data_showvariable">
      //   <value name="VARIABLE">
      //     <shadow type="data_variablemenu"></shadow>
      //   </value>
      // </block>
      var block = goog.dom.createDom('block');
      block.setAttribute('type', 'data_hidevariable');
      block.appendChild(Blockly.Variables.createVariableDom_(variableModelList[0]));
      xmlList.push(block);
    }
  }
  return xmlList;
};

/**
 * Create a dom element for a value tag with the given name attribute.
 * @param {string} name The value to use for the name attribute.
 * @return {!Element} An XML element: <value name="name"></value>
 */
Blockly.Variables.createValueDom_ = function(name) {
  var value = goog.dom.createDom('value');
  value.setAttribute('name', name);
  return value;
};

/**
 * Create a dom element for a shadow tag with the given tupe attribute.
 * @param {string} type The value to use for the type attribute.
 * @param {string} value The value to have inside the tag.
 * @return {!Element} An XML element: <shadow type="type">value</shadow>
 */
Blockly.Variables.createShadowDom_ = function(type) {
  var shadow = goog.dom.createDom('shadow');
  shadow.setAttribute('type', type);
  return shadow;
};

/**
 * Create a dom element for value tag with a shadow variable inside.
 * @param {Blockly.VariableModel} variableModel The variable to use.
 * @return {!Element} An XML element.
 */
Blockly.Variables.createVariableDom_ = function(variableModel) {
  //   <value name="VARIABLE">
  //     <shadow type="data_variablemenu">
  //       <field name="VARIABLE">variablename
  //       </field>
  //     </shadow>
  //   </value>
  var value = Blockly.Variables.createValueDom_('VARIABLE');
  var shadow = Blockly.Variables.createShadowDom_('data_variablemenu');
  var field = goog.dom.createDom('field', null, variableModel.name);
  field.setAttribute('name', 'VARIABLE');
  field.setAttribute('variableType', variableModel.type);
  field.setAttribute('id', variableModel.getId());
  shadow.appendChild(field);
  value.appendChild(shadow);
  return value;
};

/**
 * Create a dom element for value tag with a shadow text inside.
 * @return {!Element} An XML element.
 */
Blockly.Variables.createTextDom_ = function() {
  //   <value name="VALUE">
  //     <shadow type="text">
  //       <field name="TEXT">0</field>
  //     </shadow>
  //   </value>
  var value = Blockly.Variables.createValueDom_('VALUE');
  var shadow = Blockly.Variables.createShadowDom_('text');
  var field = goog.dom.createDom('field', null, '0');
  field.setAttribute('name', 'TEXT');
  shadow.appendChild(field);
  value.appendChild(shadow);
  return value;
};

/**
 * Create a dom element for value tag with a shadow number inside.
 * @return {!Element} An XML element.
 */
Blockly.Variables.createMathNumberDom_ = function() {
  //   <value name="VALUE">
  //     <shadow type="math_number">
  //       <field name="NUM">0</field>
  //     </shadow>
  //   </value>
  var value = Blockly.Variables.createValueDom_('VALUE');
  var shadow = Blockly.Variables.createShadowDom_('math_number');
  var field = goog.dom.createDom('field', null, '1');
  field.setAttribute('name', 'NUM');
  shadow.appendChild(field);
  value.appendChild(shadow);
  return value;
};

/**
 * Return the text that should be used in a field_variable or
 * field_variable_getter when no variable exists.
 * TODO: #572
 * @return {string} The text to display.
 */
Blockly.Variables.noVariableText = function() {
  return "No variable selected";
};

/**
* Return a new variable name that is not yet being used. This will try to
* generate single letter variable names in the range 'i' to 'z' to start with.
* If no unique name is located it will try 'i' to 'z', 'a' to 'h',
* then 'i2' to 'z2' etc.  Skip 'l'.
 * @param {!Blockly.Workspace} workspace The workspace to be unique in.
* @return {string} New variable name.
*/
Blockly.Variables.generateUniqueName = function(workspace) {
  var variableList = workspace.getAllVariables();
  var newName = '';
  if (variableList.length) {
    var nameSuffix = 1;
    var letters = 'ijkmnopqrstuvwxyzabcdefgh';  // No 'l'.
    var letterIndex = 0;
    var potName = letters.charAt(letterIndex);
    while (!newName) {
      var inUse = false;
      for (var i = 0; i < variableList.length; i++) {
        if (variableList[i].name.toLowerCase() == potName) {
          // This potential name is already used.
          inUse = true;
          break;
        }
      }
      if (inUse) {
        // Try the next potential name.
        letterIndex++;
        if (letterIndex == letters.length) {
          // Reached the end of the character sequence so back to 'i'.
          // a new suffix.
          letterIndex = 0;
          nameSuffix++;
        }
        potName = letters.charAt(letterIndex);
        if (nameSuffix > 1) {
          potName += nameSuffix;
        }
      } else {
        // We can use the current potential name.
        newName = potName;
      }
    }
  } else {
    newName = 'i';
  }
  return newName;
};

/**
 * Create a new variable on the given workspace.
 * @param {!Blockly.Workspace} workspace The workspace on which to create the
 *     variable.
 * @param {function(?string=)=} opt_callback A callback. It will
 *     be passed an acceptable new variable name, or null if change is to be
 *     aborted (cancel button), or undefined if an existing variable was chosen.
 */
Blockly.Variables.createVariable = function(workspace, opt_callback) {
  var promptAndCheckWithAlert = function(defaultName) {
    Blockly.Variables.promptName(Blockly.Msg.NEW_VARIABLE_TITLE, defaultName,
      function(text) {
        if (text) {
          if (workspace.getVariable(text)) {
            Blockly.alert(Blockly.Msg.VARIABLE_ALREADY_EXISTS.replace('%1',
                text.toLowerCase()),
                function() {
                  promptAndCheckWithAlert(text);  // Recurse
                });
          }
          else if (!Blockly.Procedures.isLegalName_(text, workspace)) {
            Blockly.alert(Blockly.Msg.PROCEDURE_ALREADY_EXISTS.replace('%1',
                text.toLowerCase()),
                function() {
                  promptAndCheckWithAlert(text);  // Recurse
                });
          }
          else {
            workspace.createVariable(text);
            if (opt_callback) {
              opt_callback(text);
            }
          }
        } else {
          // User canceled prompt without a value.
          if (opt_callback) {
            opt_callback(null);
          }
        }
      });
  };
  promptAndCheckWithAlert('');
};

/**
 * Prompt the user for a new variable name.
 * @param {string} promptText The string of the prompt.
 * @param {string} defaultText The default value to show in the prompt's field.
 * @param {function(?string)} callback A callback. It will be passed the new
 *     variable name, or null if the user picked something illegal.
 */
Blockly.Variables.promptName = function(promptText, defaultText, callback) {
  Blockly.prompt(promptText, defaultText, function(newVar) {
    // Merge runs of whitespace.  Strip leading and trailing whitespace.
    // Beyond this, all names are legal.
    if (newVar) {
      newVar = newVar.replace(/[\s\xa0]+/g, ' ').replace(/^ | $/g, '');
      if (newVar == Blockly.Msg.RENAME_VARIABLE ||
          newVar == Blockly.Msg.NEW_VARIABLE) {
        // Ok, not ALL names are legal...
        newVar = null;
      }
    }
    callback(newVar);
  });
};
<commit_msg>Give persistent block ids to variables in the flyout<commit_after>/**
 * @license
 * Visual Blocks Editor
 *
 * Copyright 2012 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 Utility functions for handling variables.
 * @author fraser@google.com (Neil Fraser)
 */
'use strict';

/**
 * @name Blockly.Variables
 * @namespace
 **/
goog.provide('Blockly.Variables');

goog.require('Blockly.Blocks');
goog.require('Blockly.constants');
goog.require('Blockly.VariableModel');
goog.require('Blockly.Workspace');
goog.require('goog.string');

/**
 * Constant to separate variable names from procedures and generated functions
 * when running generators.
 * @deprecated Use Blockly.VARIABLE_CATEGORY_NAME
 */
Blockly.Variables.NAME_TYPE = Blockly.VARIABLE_CATEGORY_NAME;

/**
 * Find all user-created variables that are in use in the workspace.
 * For use by generators.
 * @param {!Blockly.Block|!Blockly.Workspace} root Root block or workspace.
 * @return {!Array.<string>} Array of variable names.
 */
Blockly.Variables.allUsedVariables = function(root) {
  var blocks;
  if (root instanceof Blockly.Block) {
    // Root is Block.
    blocks = root.getDescendants();
  } else if (root instanceof Blockly.Workspace ||
      root instanceof Blockly.WorkspaceSvg) {
    // Root is Workspace.
    blocks = root.getAllBlocks();
  } else {
    throw 'Not Block or Workspace: ' + root;
  }

  var ignorableName = Blockly.Variables.noVariableText();

  var variableHash = Object.create(null);
  // Iterate through every block and add each variable to the hash.
  for (var x = 0; x < blocks.length; x++) {
    var blockVariables = blocks[x].getVars();
    if (blockVariables) {
      for (var y = 0; y < blockVariables.length; y++) {
        var varName = blockVariables[y];
        // Variable name may be null if the block is only half-built.
        if (varName && varName.toLowerCase() != ignorableName) {
          variableHash[varName.toLowerCase()] = varName;
        }
      }
    }
  }
  // Flatten the hash into a list.
  var variableList = [];
  for (var name in variableHash) {
    variableList.push(variableHash[name]);
  }
  return variableList;
};

/**
 * Find all variables that the user has created through the workspace or
 * toolbox.  For use by generators.
 * @param {!Blockly.Workspace} root The workspace to inspect.
 * @return {!Array.<Blockly.VariableModel>} Array of variable models.
 */
Blockly.Variables.allVariables = function(root) {
  if (root instanceof Blockly.Block) {
    // Root is Block.
    console.warn('Deprecated call to Blockly.Variables.allVariables ' +
                 'with a block instead of a workspace.  You may want ' +
                 'Blockly.Variables.allUsedVariables');
    return {};
  }
  return root.getAllVariables();
};

/**
 * Construct the blocks required by the flyout for the variable category.
 * @param {!Blockly.Workspace} workspace The workspace contianing variables.
 * @return {!Array.<!Element>} Array of XML block elements.
 */
Blockly.Variables.flyoutCategory = function(workspace) {
  var variableModelList = workspace.getVariablesOfType('');
  variableModelList.sort(Blockly.VariableModel.compareByName);

  var xmlList = [];
  var button = goog.dom.createDom('button');
  button.setAttribute('text', Blockly.Msg.NEW_VARIABLE);
  button.setAttribute('callbackKey', 'CREATE_VARIABLE');

  workspace.registerButtonCallback('CREATE_VARIABLE', function(button) {
    Blockly.Variables.createVariable(button.getTargetWorkspace());
  });

  xmlList.push(button);

  for (var i = 0; i < variableModelList.length; i++) {
    if (Blockly.Blocks['data_variable']) {
      // <block type="data_variable">
      //    <field name="VARIABLE" variableType="" id="">variablename</field>
      // </block>
      var block = goog.dom.createDom('block');
      block.setAttribute('type', 'data_variable');
      block.setAttribute('gap', 8);
      block.setAttribute('id', 'VAR_' + variableModelList[i].name);

      var field = goog.dom.createDom('field', null, variableModelList[i].name);
      field.setAttribute('name', 'VARIABLE');
      field.setAttribute('variableType', variableModelList[i].type);
      field.setAttribute('id', variableModelList[i].getId());
      block.appendChild(field);

      xmlList.push(block);
    }
  }

  if (xmlList.length > 1) { // The button is always there.
    xmlList[xmlList.length - 1].setAttribute('gap', 24);

    if (Blockly.Blocks['data_setvariableto']) {
      // <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>
      var block = goog.dom.createDom('block');
      block.setAttribute('type', 'data_setvariableto');
      block.setAttribute('gap', 8);
      block.appendChild(Blockly.Variables.createVariableDom_(variableModelList[0]));
      block.appendChild(Blockly.Variables.createTextDom_());
      xmlList.push(block);
    }
    if (Blockly.Blocks['data_changevariableby']) {
      // <block type="data_changevariableby">
      //   <value name="VARIABLE">
      //    <shadow type="data_variablemenu"></shadow>
      //   </value>
      //   <value name="VALUE">
      //     <shadow type="math_number">
      //       <field name="NUM">0</field>
      //     </shadow>
      //   </value>
      // </block>
      var block = goog.dom.createDom('block');
      block.setAttribute('type', 'data_changevariableby');
      block.setAttribute('gap', 8);
      block.appendChild(Blockly.Variables.createVariableDom_(variableModelList[0]));
      block.appendChild(Blockly.Variables.createMathNumberDom_());
      xmlList.push(block);
    }
    if (Blockly.Blocks['data_showvariable']) {
      // <block type="data_showvariable">
      //   <value name="VARIABLE">
      //     <shadow type="data_variablemenu"></shadow>
      //   </value>
      // </block>
      var block = goog.dom.createDom('block');
      block.setAttribute('type', 'data_showvariable');
      block.setAttribute('gap', 8);
      block.appendChild(Blockly.Variables.createVariableDom_(variableModelList[0]));
      xmlList.push(block);
    }
    if (Blockly.Blocks['data_hidevariable']) {
      // <block type="data_showvariable">
      //   <value name="VARIABLE">
      //     <shadow type="data_variablemenu"></shadow>
      //   </value>
      // </block>
      var block = goog.dom.createDom('block');
      block.setAttribute('type', 'data_hidevariable');
      block.appendChild(Blockly.Variables.createVariableDom_(variableModelList[0]));
      xmlList.push(block);
    }
  }
  return xmlList;
};

/**
 * Create a dom element for a value tag with the given name attribute.
 * @param {string} name The value to use for the name attribute.
 * @return {!Element} An XML element: <value name="name"></value>
 */
Blockly.Variables.createValueDom_ = function(name) {
  var value = goog.dom.createDom('value');
  value.setAttribute('name', name);
  return value;
};

/**
 * Create a dom element for a shadow tag with the given tupe attribute.
 * @param {string} type The value to use for the type attribute.
 * @param {string} value The value to have inside the tag.
 * @return {!Element} An XML element: <shadow type="type">value</shadow>
 */
Blockly.Variables.createShadowDom_ = function(type) {
  var shadow = goog.dom.createDom('shadow');
  shadow.setAttribute('type', type);
  return shadow;
};

/**
 * Create a dom element for value tag with a shadow variable inside.
 * @param {Blockly.VariableModel} variableModel The variable to use.
 * @return {!Element} An XML element.
 */
Blockly.Variables.createVariableDom_ = function(variableModel) {
  //   <value name="VARIABLE">
  //     <shadow type="data_variablemenu">
  //       <field name="VARIABLE">variablename
  //       </field>
  //     </shadow>
  //   </value>
  var value = Blockly.Variables.createValueDom_('VARIABLE');
  var shadow = Blockly.Variables.createShadowDom_('data_variablemenu');
  var field = goog.dom.createDom('field', null, variableModel.name);
  field.setAttribute('name', 'VARIABLE');
  field.setAttribute('variableType', variableModel.type);
  field.setAttribute('id', variableModel.getId());
  shadow.appendChild(field);
  value.appendChild(shadow);
  return value;
};

/**
 * Create a dom element for value tag with a shadow text inside.
 * @return {!Element} An XML element.
 */
Blockly.Variables.createTextDom_ = function() {
  //   <value name="VALUE">
  //     <shadow type="text">
  //       <field name="TEXT">0</field>
  //     </shadow>
  //   </value>
  var value = Blockly.Variables.createValueDom_('VALUE');
  var shadow = Blockly.Variables.createShadowDom_('text');
  var field = goog.dom.createDom('field', null, '0');
  field.setAttribute('name', 'TEXT');
  shadow.appendChild(field);
  value.appendChild(shadow);
  return value;
};

/**
 * Create a dom element for value tag with a shadow number inside.
 * @return {!Element} An XML element.
 */
Blockly.Variables.createMathNumberDom_ = function() {
  //   <value name="VALUE">
  //     <shadow type="math_number">
  //       <field name="NUM">0</field>
  //     </shadow>
  //   </value>
  var value = Blockly.Variables.createValueDom_('VALUE');
  var shadow = Blockly.Variables.createShadowDom_('math_number');
  var field = goog.dom.createDom('field', null, '1');
  field.setAttribute('name', 'NUM');
  shadow.appendChild(field);
  value.appendChild(shadow);
  return value;
};

/**
 * Return the text that should be used in a field_variable or
 * field_variable_getter when no variable exists.
 * TODO: #572
 * @return {string} The text to display.
 */
Blockly.Variables.noVariableText = function() {
  return "No variable selected";
};

/**
* Return a new variable name that is not yet being used. This will try to
* generate single letter variable names in the range 'i' to 'z' to start with.
* If no unique name is located it will try 'i' to 'z', 'a' to 'h',
* then 'i2' to 'z2' etc.  Skip 'l'.
 * @param {!Blockly.Workspace} workspace The workspace to be unique in.
* @return {string} New variable name.
*/
Blockly.Variables.generateUniqueName = function(workspace) {
  var variableList = workspace.getAllVariables();
  var newName = '';
  if (variableList.length) {
    var nameSuffix = 1;
    var letters = 'ijkmnopqrstuvwxyzabcdefgh';  // No 'l'.
    var letterIndex = 0;
    var potName = letters.charAt(letterIndex);
    while (!newName) {
      var inUse = false;
      for (var i = 0; i < variableList.length; i++) {
        if (variableList[i].name.toLowerCase() == potName) {
          // This potential name is already used.
          inUse = true;
          break;
        }
      }
      if (inUse) {
        // Try the next potential name.
        letterIndex++;
        if (letterIndex == letters.length) {
          // Reached the end of the character sequence so back to 'i'.
          // a new suffix.
          letterIndex = 0;
          nameSuffix++;
        }
        potName = letters.charAt(letterIndex);
        if (nameSuffix > 1) {
          potName += nameSuffix;
        }
      } else {
        // We can use the current potential name.
        newName = potName;
      }
    }
  } else {
    newName = 'i';
  }
  return newName;
};

/**
 * Create a new variable on the given workspace.
 * @param {!Blockly.Workspace} workspace The workspace on which to create the
 *     variable.
 * @param {function(?string=)=} opt_callback A callback. It will
 *     be passed an acceptable new variable name, or null if change is to be
 *     aborted (cancel button), or undefined if an existing variable was chosen.
 */
Blockly.Variables.createVariable = function(workspace, opt_callback) {
  var promptAndCheckWithAlert = function(defaultName) {
    Blockly.Variables.promptName(Blockly.Msg.NEW_VARIABLE_TITLE, defaultName,
      function(text) {
        if (text) {
          if (workspace.getVariable(text)) {
            Blockly.alert(Blockly.Msg.VARIABLE_ALREADY_EXISTS.replace('%1',
                text.toLowerCase()),
                function() {
                  promptAndCheckWithAlert(text);  // Recurse
                });
          }
          else if (!Blockly.Procedures.isLegalName_(text, workspace)) {
            Blockly.alert(Blockly.Msg.PROCEDURE_ALREADY_EXISTS.replace('%1',
                text.toLowerCase()),
                function() {
                  promptAndCheckWithAlert(text);  // Recurse
                });
          }
          else {
            workspace.createVariable(text);
            if (opt_callback) {
              opt_callback(text);
            }
          }
        } else {
          // User canceled prompt without a value.
          if (opt_callback) {
            opt_callback(null);
          }
        }
      });
  };
  promptAndCheckWithAlert('');
};

/**
 * Prompt the user for a new variable name.
 * @param {string} promptText The string of the prompt.
 * @param {string} defaultText The default value to show in the prompt's field.
 * @param {function(?string)} callback A callback. It will be passed the new
 *     variable name, or null if the user picked something illegal.
 */
Blockly.Variables.promptName = function(promptText, defaultText, callback) {
  Blockly.prompt(promptText, defaultText, function(newVar) {
    // Merge runs of whitespace.  Strip leading and trailing whitespace.
    // Beyond this, all names are legal.
    if (newVar) {
      newVar = newVar.replace(/[\s\xa0]+/g, ' ').replace(/^ | $/g, '');
      if (newVar == Blockly.Msg.RENAME_VARIABLE ||
          newVar == Blockly.Msg.NEW_VARIABLE) {
        // Ok, not ALL names are legal...
        newVar = null;
      }
    }
    callback(newVar);
  });
};
<|endoftext|>


<commit_before>var request = require("request");

/*
 * DiscoveryClient constructor.
 * Creates a new uninitialized DiscoveryClient.
 */
function DiscoveryClient(host, options) {
  this.host = host;
  this.state = {announcements: {}};
  this.errorHandlers = [this._backoff.bind(this)];
  this.watchers = [this._update.bind(this), this._unbackoff.bind(this)];
  this.announcements = [];
  this.backoff = 1;
  this.logger = (options && options.logger) || require("ot-logger");
}

/* Increase the watch backoff interval */
DiscoveryClient.prototype._backoff = function () {
  this.backoff = Math.min(this.backoff * 2, 10240);
};

/* Reset the watch backoff interval */
DiscoveryClient.prototype._unbackoff = function() {
  this.backoff = 1;
};

DiscoveryClient.prototype._randomServer = function() {
  return this.servers[Math.floor(Math.random()*this.servers.length)];
};

/* Consume a WatchResult from the discovery server and update internal state */
DiscoveryClient.prototype._update = function (update) {
  var disco = this;
  if (update.fullUpdate) {
    Object.keys(disco.state.announcements).forEach(function (id) { delete disco.state.announcements[id]; });
  }
  disco.state.index = update.index;
  update.deletes.forEach(function (id) { delete disco.state.announcements[id]; });
  update.updates.forEach(function (announcement) { disco.state.announcements[announcement.announcementId] = announcement; });
};

/* Connect to the Discovery servers given the endpoint of any one of them */
DiscoveryClient.prototype.connect = function (onComplete) {
  var disco = this;
  request({
    url: "http://" + this.host + "/watch",
    json: true
  }, function (error, response, update) {
    if (error) {
      onComplete(error);
      return;
    }
    if (response.statusCode != 200) {
      onComplete(new Error("Unable to initiate discovery: " + update), undefined, undefined);
      return;
    }
    if (!update.fullUpdate) {
      onComplete(new Error("Expecting a full update: " + update), undefined, undefined);
      return;
    }
    disco._update(update);

    disco.servers = [];

    Object.keys(disco.state.announcements).forEach(function (id) {
      var announcement = disco.state.announcements[id];
      if (announcement.serviceType == "discovery") {
        disco.servers.push(announcement.serviceUri);
      }
    });

    disco._schedule();
    setInterval(disco._announce.bind(disco), 10000);
    onComplete(undefined, disco.host, disco.servers);
  });
};

/* Register a callback on every discovery update */
DiscoveryClient.prototype.onUpdate = function (watcher) {
  this.watchers.push(watcher);
};

/* Register a callback on every error */
DiscoveryClient.prototype.onError = function (handler) {
  this.errorHandlers.push(handler);
};

/* Internal scheduling method.  Schedule the next poll in the event loop */
DiscoveryClient.prototype._schedule = function() {
  var c = this.poll.bind(this);
  if (this.backoff <= 1) {
    setImmediate(c);
  } else {
    setTimeout(c, this.backoff).unref();
  }
};

/* Long-poll the discovery server for changes */
DiscoveryClient.prototype.poll = function () {
  var disco = this;
  var server = this._randomServer();
  var url = server + "/watch?since=" + (this.state.index + 1);
  request({
    url: url,
    json: true
  }, function (error, response, body) {
    if (error) {
      disco.errorHandlers.forEach(function (h) { h(error); });
      disco._schedule();
      return;
    }
    if (response.statusCode == 204) {
      disco._schedule();
      return;
    }
    if (response.statusCode != 200) {
      var err = new Error("Bad status code " + response.statusCode + " from watch: " + response);
      disco.errorHandlers.forEach(function (h) { h(err); });
      disco._schedule();
      return;
    }
    disco.watchers.forEach(function (w) { w(body); });
    disco._schedule();
  });
};

/* Lookup a service by service type!
 * Accepts one of:
 *   serviceType as string
 *   serviceType:feature as string
 *   predicate function over announcement object
 */
DiscoveryClient.prototype.findAll = function (predicate) {
  var disco = this;
  var candidates = [];

  if (typeof(predicate) != "function") {
    var serviceType = predicate;
    predicate = function(a) {
      return a.serviceType == serviceType || a.serviceType + ":" + a.feature == serviceType;
    };
  }

  Object.keys(disco.state.announcements).forEach(function (id) {
    var a = disco.state.announcements[id];
    if (predicate(a)) {
      candidates.push(a.serviceUri);
    }
  });

  return candidates;
};

DiscoveryClient.prototype.find = function (predicate) {
  var candidates = this.findAll(predicate);
  if (candidates.length === 0) {
    return undefined;
  }
  return candidates[Math.floor(Math.random()*candidates.length)];
};

DiscoveryClient.prototype._announce = function() {
  var disco = this;
  function cb(error, announcement) {
    if (error) {
      disco.errorHandlers.forEach(function (h) { h(error); });
    }
  }
  this.announcements.forEach(function (a) {
    disco._singleAnnounce(a, cb);
  });
};

DiscoveryClient.prototype._singleAnnounce = function (announcement, cb) {
  var server = this._randomServer();
  request({
    url: server + "/announcement",
    method: "POST",
    json: true,
    body: announcement
  }, function (error, response, body) {
    if (error) {
      cb(error);
      return;
    }
    if (response.statusCode != 201) {
      cb(new Error("During announce, bad status code " + response.statusCode + ": " + body));
      return;
    }
    cb(undefined, body);
  });
};

/* Announce ourselves to the registry */
DiscoveryClient.prototype.announce = function (announcement, cb) {
  var disco = this;
  this._singleAnnounce(announcement, function(error, a) {
    if (error) {
      cb(error);
      return;
    }
    disco.logger.log("Announced as " + a);
    disco.announcements.push(a);
    cb(undefined, a);
  });
};

/* Remove a previous announcement.  The passed object *must* be the
 * lease as returned by the 'announce' callback. */
DiscoveryClient.prototype.unannounce = function (announcement, callback) {
  var disco = this;
  var server = disco._randomServer();
  var url = server + "/announcement/" + announcement.announcementId;
  disco.announcements.splice(disco.announcements.indexOf(announcement), 1);
  request({
    url: url,
    method: "DELETE"
  }, function (error, response, body) {
    if (error) {
      disco.logger.error(error);
    } else {
      disco.logger.log("Unannounce DELETE '" + url + "' returned " + response.statusCode + ": " + body);
    }
    if (callback) {
      callback();
    }
  });
};

module.exports = DiscoveryClient;
<commit_msg>stringify objects when logging<commit_after>var request = require("request");

/*
 * DiscoveryClient constructor.
 * Creates a new uninitialized DiscoveryClient.
 */
function DiscoveryClient(host, options) {
  this.host = host;
  this.state = {announcements: {}};
  this.errorHandlers = [this._backoff.bind(this)];
  this.watchers = [this._update.bind(this), this._unbackoff.bind(this)];
  this.announcements = [];
  this.backoff = 1;
  this.logger = (options && options.logger) || require("ot-logger");
}

/* Increase the watch backoff interval */
DiscoveryClient.prototype._backoff = function () {
  this.backoff = Math.min(this.backoff * 2, 10240);
};

/* Reset the watch backoff interval */
DiscoveryClient.prototype._unbackoff = function() {
  this.backoff = 1;
};

DiscoveryClient.prototype._randomServer = function() {
  return this.servers[Math.floor(Math.random()*this.servers.length)];
};

/* Consume a WatchResult from the discovery server and update internal state */
DiscoveryClient.prototype._update = function (update) {
  var disco = this;
  if (update.fullUpdate) {
    Object.keys(disco.state.announcements).forEach(function (id) { delete disco.state.announcements[id]; });
  }
  disco.state.index = update.index;
  update.deletes.forEach(function (id) { delete disco.state.announcements[id]; });
  update.updates.forEach(function (announcement) { disco.state.announcements[announcement.announcementId] = announcement; });
};

/* Connect to the Discovery servers given the endpoint of any one of them */
DiscoveryClient.prototype.connect = function (onComplete) {
  var disco = this;
  request({
    url: "http://" + this.host + "/watch",
    json: true
  }, function (error, response, update) {
    if (error) {
      onComplete(error);
      return;
    }
    if (response.statusCode != 200) {
      onComplete(new Error("Unable to initiate discovery: " + JSON.stringify(update)), undefined, undefined);
      return;
    }

    if (!update.fullUpdate) {
      onComplete(new Error("Expecting a full update: " + JSON.stringify(update)), undefined, undefined);
      return;
    }
    disco._update(update);

    disco.servers = [];

    Object.keys(disco.state.announcements).forEach(function (id) {
      var announcement = disco.state.announcements[id];
      if (announcement.serviceType == "discovery") {
        disco.servers.push(announcement.serviceUri);
      }
    });

    disco._schedule();
    setInterval(disco._announce.bind(disco), 10000);
    onComplete(undefined, disco.host, disco.servers);
  });
};

/* Register a callback on every discovery update */
DiscoveryClient.prototype.onUpdate = function (watcher) {
  this.watchers.push(watcher);
};

/* Register a callback on every error */
DiscoveryClient.prototype.onError = function (handler) {
  this.errorHandlers.push(handler);
};

/* Internal scheduling method.  Schedule the next poll in the event loop */
DiscoveryClient.prototype._schedule = function() {
  var c = this.poll.bind(this);
  if (this.backoff <= 1) {
    setImmediate(c);
  } else {
    setTimeout(c, this.backoff).unref();
  }
};

/* Long-poll the discovery server for changes */
DiscoveryClient.prototype.poll = function () {
  var disco = this;
  var server = this._randomServer();
  var url = server + "/watch?since=" + (this.state.index + 1);
  request({
    url: url,
    json: true
  }, function (error, response, body) {
    if (error) {
      disco.errorHandlers.forEach(function (h) { h(error); });
      disco._schedule();
      return;
    }
    if (response.statusCode == 204) {
      disco._schedule();
      return;
    }
    if (response.statusCode != 200) {
      var err = new Error("Bad status code " + response.statusCode + " from watch: " + response);
      disco.errorHandlers.forEach(function (h) { h(err); });
      disco._schedule();
      return;
    }
    disco.watchers.forEach(function (w) { w(body); });
    disco._schedule();
  });
};

/* Lookup a service by service type!
 * Accepts one of:
 *   serviceType as string
 *   serviceType:feature as string
 *   predicate function over announcement object
 */
DiscoveryClient.prototype.findAll = function (predicate) {
  var disco = this;
  var candidates = [];

  if (typeof(predicate) != "function") {
    var serviceType = predicate;
    predicate = function(a) {
      return a.serviceType == serviceType || a.serviceType + ":" + a.feature == serviceType;
    };
  }

  Object.keys(disco.state.announcements).forEach(function (id) {
    var a = disco.state.announcements[id];
    if (predicate(a)) {
      candidates.push(a.serviceUri);
    }
  });

  return candidates;
};

DiscoveryClient.prototype.find = function (predicate) {
  var candidates = this.findAll(predicate);
  if (candidates.length === 0) {
    return undefined;
  }
  return candidates[Math.floor(Math.random()*candidates.length)];
};

DiscoveryClient.prototype._announce = function() {
  var disco = this;
  function cb(error, announcement) {
    if (error) {
      disco.errorHandlers.forEach(function (h) { h(error); });
    }
  }
  this.announcements.forEach(function (a) {
    disco._singleAnnounce(a, cb);
  });
};

DiscoveryClient.prototype._singleAnnounce = function (announcement, cb) {
  var server = this._randomServer();
  request({
    url: server + "/announcement",
    method: "POST",
    json: true,
    body: announcement
  }, function (error, response, body) {
    if (error) {
      cb(error);
      return;
    }
    if (response.statusCode != 201) {
      cb(new Error("During announce, bad status code " + response.statusCode + ": " + JSON.stringify(body)));
      return;
    }
    cb(undefined, body);
  });
};

/* Announce ourselves to the registry */
DiscoveryClient.prototype.announce = function (announcement, cb) {
  var disco = this;
  this._singleAnnounce(announcement, function(error, a) {
    if (error) {
      cb(error);
      return;
    }
    disco.logger.log("Announced as " + a);
    disco.announcements.push(a);
    cb(undefined, a);
  });
};

/* Remove a previous announcement.  The passed object *must* be the
 * lease as returned by the 'announce' callback. */
DiscoveryClient.prototype.unannounce = function (announcement, callback) {
  var disco = this;
  var server = disco._randomServer();
  var url = server + "/announcement/" + announcement.announcementId;
  disco.announcements.splice(disco.announcements.indexOf(announcement), 1);
  request({
    url: url,
    method: "DELETE"
  }, function (error, response, body) {
    if (error) {
      disco.logger.error(error);
    } else {
      disco.logger.log("Unannounce DELETE '" + url + "' returned " + response.statusCode + ": " + JSON.stringify(body));
    }
    if (callback) {
      callback();
    }
  });
};

module.exports = DiscoveryClient;
<|endoftext|>


<commit_before>var request = require("request");

/*
 * DiscoveryClient constructor.
 * Creates a new uninitialized DiscoveryClient.
 */
function DiscoveryClient(host, options) {
  this.host = host;
  this.state = {announcements: {}};
  this.logger = (options && options.logger) || require("ot-logger");
  this.errorHandlers = [this.logger.log.bind(this.logger, 'error', 'Discovery error: ')];
  this.watchers = [this._update.bind(this), this.logger.log.bind(this.logger, 'debug', 'Discovery update: ')];
  this.announcements = [];
  this.backoff = 1;
}

/* Increase the watch backoff interval */
DiscoveryClient.prototype._backoff = function () {
  this.backoff = Math.min(this.backoff * 2, 10240);
};

/* Reset the watch backoff interval */
DiscoveryClient.prototype._unbackoff = function() {
  this.backoff = 1;
};

DiscoveryClient.prototype._randomServer = function() {
  return this.servers[Math.floor(Math.random()*this.servers.length)];
};

/* Consume a WatchResult from the discovery server and update internal state */
DiscoveryClient.prototype._update = function (update) {
  var disco = this;
  if (update.fullUpdate) {
    Object.keys(disco.state.announcements).forEach(function (id) { delete disco.state.announcements[id]; });
  }
  disco.state.index = update.index;
  update.deletes.forEach(function (id) { delete disco.state.announcements[id]; });
  update.updates.forEach(function (announcement) { disco.state.announcements[announcement.announcementId] = announcement; });
};

/* Connect to the Discovery servers given the endpoint of any one of them */
DiscoveryClient.prototype.connect = function (onComplete) {
  var disco = this;
  request({
    url: "http://" + this.host + "/watch",
    json: true
  }, function (error, response, update) {
    if (error) {
      onComplete(error);
      return;
    }
    if (response.statusCode != 200) {
      onComplete(new Error("Unable to initiate discovery: " + JSON.stringify(update)), undefined, undefined);
      return;
    }

    if (!update.fullUpdate) {
      onComplete(new Error("Expecting a full update: " + JSON.stringify(update)), undefined, undefined);
      return;
    }

    disco.logger.log('debug', 'Discovery update: %s', JSON.stringify(update));
    disco._update(update);

    disco.servers = [];

    Object.keys(disco.state.announcements).forEach(function (id) {
      var announcement = disco.state.announcements[id];
      if (announcement.serviceType == "discovery") {
        disco.servers.push(announcement.serviceUri);
      }
    });

    disco._schedulePoll();
    setInterval(disco._announce.bind(disco), 10000);
    onComplete(undefined, disco.host, disco.servers);
  });
};

DiscoveryClient.prototype.reconnect = function (onComplete) {
  var disco = this;
  disco.logger.log('info', 'Attempting to reconnect to Discovery on %s', disco.host);
  disco.reconnectScheduled = false;
  disco.state.index = -1;
  request({
    url: "http://" + disco.host + "/watch?since=" + (this.state.index),
    json: true
  }, function (error, response, update) {
    if (error) {
      disco.logger.log('error', 'Could not reconnect to Discovery on: %s', disco.host);
      disco._scheduleReconnect();
      return;
    }
    if (response.statusCode != 200) {
      disco.logger.log('error', 'Could not reconnect to Discovery on: %s', disco.host);
      disco._scheduleReconnect();
      return;
    }

    if (!update.fullUpdate) {
      disco.logger.log('error', 'Expecting a full update: %s', JSON.stringify(update));
      disco._scheduleReconnect();
      return;
    }

    disco.logger.log('info', 'Reconnected toDiscovery on %s', disco.host);

    disco._update(update);
    disco.servers = [];
    Object.keys(disco.state.announcements).forEach(function (id) {
      var announcement = disco.state.announcements[id];
      if (announcement.serviceType == "discovery") {
        disco.servers.push(announcement.serviceUri);
      }
    });

    disco._unbackoff();
    disco._schedulePoll();
  });
};

/* Register a callback on every discovery update */
DiscoveryClient.prototype.onUpdate = function (watcher) {
  this.watchers.push(watcher);
};

/* Register a callback on every error */
DiscoveryClient.prototype.onError = function (handler) {
  this.errorHandlers.push(handler);
};

/* Internal scheduling method.  Schedule the next poll in the event loop */
DiscoveryClient.prototype._schedulePoll = function () {
  if (!this.servers.length) {
    this._scheduleReconnect();
    return;
  }
  setImmediate(this.poll.bind(this));
};

DiscoveryClient.prototype._scheduleReconnect = function () {
  if (this.reconnectScheduled) {
    return;
  }

  this.reconnectScheduled = true;
  var c = this.reconnect.bind(this);
  if (this.backoff <= 1) {
    setImmediate(c);
  } else {
    setTimeout(c, this.backoff).unref();
  }
  this._backoff();
};

/* Long-poll the discovery server for changes */
DiscoveryClient.prototype.poll = function () {
  var disco = this;
  var server = this._randomServer();
  var url = server + "/watch?since=" + (this.state.index + 1);
  request({
    url: url,
    json: true
  }, function (error, response, body) {
    if (error) {
      disco.errorHandlers.forEach(function (h) { h(error); });
      disco.dropServer(server);
      disco._schedulePoll();
      return;
    }
    if (response.statusCode == 204) {
      disco._schedulePoll();
      return;
    }
    if (response.statusCode != 200) {
      var err = new Error("Bad status code " + response.statusCode + " from watch: " + response);
      disco.errorHandlers.forEach(function (h) { h(err); });
      disco.dropServer(server);
      disco._schedulePoll();
      return;
    }
    disco.watchers.forEach(function (w) { w(body); });
    disco._schedulePoll();
  });
};

DiscoveryClient.prototype.dropServer = function (server) {
  this.logger.log('info', 'Dropping discovery server %s', server);
  this.servers.splice(this.servers.indexOf(server), 1);
};

/* Lookup a service by service type!
 * Accepts one of:
 *   serviceType as string
 *   serviceType:feature as string
 *   predicate function over announcement object
 */
DiscoveryClient.prototype.findAll = function (predicate) {
  var disco = this;
  var candidates = [];

  if (typeof(predicate) != "function") {
    var serviceType = predicate;
    predicate = function(a) {
      return a.serviceType == serviceType || a.serviceType + ":" + a.feature == serviceType;
    };
  }

  Object.keys(disco.state.announcements).forEach(function (id) {
    var a = disco.state.announcements[id];
    if (predicate(a)) {
      candidates.push(a.serviceUri);
    }
  });

  return candidates;
};

DiscoveryClient.prototype.find = function (predicate) {
  var candidates = this.findAll(predicate);
  if (candidates.length === 0) {
    return undefined;
  }
  return candidates[Math.floor(Math.random()*candidates.length)];
};

DiscoveryClient.prototype._announce = function() {
  var disco = this;
  function cb(error, announcement) {
    if (error) {
      disco.errorHandlers.forEach(function (h) { h(error); });
    }
  }
  this.announcements.forEach(function (a) {
    disco._singleAnnounce(a, cb);
  });
};

DiscoveryClient.prototype._singleAnnounce = function (announcement, cb) {
  var server = this._randomServer();
  if (!server) {
    this.logger.log('info', 'Cannot announce. No discovery servers available');
    cb(new Error('Cannot announce. No discovery servers available'));
    return;
  }
  request({
    url: server + "/announcement",
    method: "POST",
    json: true,
    body: announcement
  }, function (error, response, body) {
    if (error) {
      cb(error);
      return;
    }
    if (response.statusCode != 201) {
      cb(new Error("During announce, bad status code " + response.statusCode + ": " + JSON.stringify(body)));
      return;
    }
    cb(undefined, body);
  });
};

/* Announce ourselves to the registry */
DiscoveryClient.prototype.announce = function (announcement, cb) {
  var disco = this;
  this._singleAnnounce(announcement, function(error, a) {
    if (error) {
      cb(error);
      return;
    }
    disco.logger.log("Announced as " + JSON.stringify(a));
    disco.announcements.push(a);
    cb(undefined, a);
  });
};

/* Remove a previous announcement.  The passed object *must* be the
 * lease as returned by the 'announce' callback. */
DiscoveryClient.prototype.unannounce = function (announcement, callback) {
  var disco = this;
  var server = disco._randomServer();
  var url = server + "/announcement/" + announcement.announcementId;
  disco.announcements.splice(disco.announcements.indexOf(announcement), 1);
  request({
    url: url,
    method: "DELETE"
  }, function (error, response, body) {
    if (error) {
      disco.logger.error(error);
    } else {
      disco.logger.log("Unannounce DELETE '" + url + "' returned " + response.statusCode + ": " + JSON.stringify(body));
    }
    if (callback) {
      callback();
    }
  });
};

module.exports = DiscoveryClient;
<commit_msg>Log announcing<commit_after>var request = require("request");

/*
 * DiscoveryClient constructor.
 * Creates a new uninitialized DiscoveryClient.
 */
function DiscoveryClient(host, options) {
  this.host = host;
  this.state = {announcements: {}};
  this.logger = (options && options.logger) || require("ot-logger");
  this.errorHandlers = [this.logger.log.bind(this.logger, 'error', 'Discovery error: ')];
  this.watchers = [this._update.bind(this), this.logger.log.bind(this.logger, 'debug', 'Discovery update: ')];
  this.announcements = [];
  this.backoff = 1;
}

/* Increase the watch backoff interval */
DiscoveryClient.prototype._backoff = function () {
  this.backoff = Math.min(this.backoff * 2, 10240);
};

/* Reset the watch backoff interval */
DiscoveryClient.prototype._unbackoff = function() {
  this.backoff = 1;
};

DiscoveryClient.prototype._randomServer = function() {
  return this.servers[Math.floor(Math.random()*this.servers.length)];
};

/* Consume a WatchResult from the discovery server and update internal state */
DiscoveryClient.prototype._update = function (update) {
  var disco = this;
  if (update.fullUpdate) {
    Object.keys(disco.state.announcements).forEach(function (id) { delete disco.state.announcements[id]; });
  }
  disco.state.index = update.index;
  update.deletes.forEach(function (id) { delete disco.state.announcements[id]; });
  update.updates.forEach(function (announcement) { disco.state.announcements[announcement.announcementId] = announcement; });
};

/* Connect to the Discovery servers given the endpoint of any one of them */
DiscoveryClient.prototype.connect = function (onComplete) {
  var disco = this;
  request({
    url: "http://" + this.host + "/watch",
    json: true
  }, function (error, response, update) {
    if (error) {
      onComplete(error);
      return;
    }
    if (response.statusCode != 200) {
      onComplete(new Error("Unable to initiate discovery: " + JSON.stringify(update)), undefined, undefined);
      return;
    }

    if (!update.fullUpdate) {
      onComplete(new Error("Expecting a full update: " + JSON.stringify(update)), undefined, undefined);
      return;
    }

    disco.logger.log('debug', 'Discovery update: %s', JSON.stringify(update));
    disco._update(update);

    disco.servers = [];

    Object.keys(disco.state.announcements).forEach(function (id) {
      var announcement = disco.state.announcements[id];
      if (announcement.serviceType == "discovery") {
        disco.servers.push(announcement.serviceUri);
      }
    });

    disco._schedulePoll();
    setInterval(disco._announce.bind(disco), 10000);
    onComplete(undefined, disco.host, disco.servers);
  });
};

DiscoveryClient.prototype.reconnect = function (onComplete) {
  var disco = this;
  disco.logger.log('info', 'Attempting to reconnect to Discovery on %s', disco.host);
  disco.reconnectScheduled = false;
  disco.state.index = -1;
  request({
    url: "http://" + disco.host + "/watch?since=" + (this.state.index),
    json: true
  }, function (error, response, update) {
    if (error) {
      disco.logger.log('error', 'Could not reconnect to Discovery on: %s', disco.host);
      disco._scheduleReconnect();
      return;
    }
    if (response.statusCode != 200) {
      disco.logger.log('error', 'Could not reconnect to Discovery on: %s', disco.host);
      disco._scheduleReconnect();
      return;
    }

    if (!update.fullUpdate) {
      disco.logger.log('error', 'Expecting a full update: %s', JSON.stringify(update));
      disco._scheduleReconnect();
      return;
    }

    disco.logger.log('info', 'Reconnected toDiscovery on %s', disco.host);

    disco._update(update);
    disco.servers = [];
    Object.keys(disco.state.announcements).forEach(function (id) {
      var announcement = disco.state.announcements[id];
      if (announcement.serviceType == "discovery") {
        disco.servers.push(announcement.serviceUri);
      }
    });

    disco._unbackoff();
    disco._schedulePoll();
  });
};

/* Register a callback on every discovery update */
DiscoveryClient.prototype.onUpdate = function (watcher) {
  this.watchers.push(watcher);
};

/* Register a callback on every error */
DiscoveryClient.prototype.onError = function (handler) {
  this.errorHandlers.push(handler);
};

/* Internal scheduling method.  Schedule the next poll in the event loop */
DiscoveryClient.prototype._schedulePoll = function () {
  if (!this.servers.length) {
    this._scheduleReconnect();
    return;
  }
  setImmediate(this.poll.bind(this));
};

DiscoveryClient.prototype._scheduleReconnect = function () {
  if (this.reconnectScheduled) {
    return;
  }

  this.reconnectScheduled = true;
  var c = this.reconnect.bind(this);
  if (this.backoff <= 1) {
    setImmediate(c);
  } else {
    setTimeout(c, this.backoff).unref();
  }
  this._backoff();
};

/* Long-poll the discovery server for changes */
DiscoveryClient.prototype.poll = function () {
  var disco = this;
  var server = this._randomServer();
  var url = server + "/watch?since=" + (this.state.index + 1);
  request({
    url: url,
    json: true
  }, function (error, response, body) {
    if (error) {
      disco.errorHandlers.forEach(function (h) { h(error); });
      disco.dropServer(server);
      disco._schedulePoll();
      return;
    }
    if (response.statusCode == 204) {
      disco._schedulePoll();
      return;
    }
    if (response.statusCode != 200) {
      var err = new Error("Bad status code " + response.statusCode + " from watch: " + response);
      disco.errorHandlers.forEach(function (h) { h(err); });
      disco.dropServer(server);
      disco._schedulePoll();
      return;
    }
    disco.watchers.forEach(function (w) { w(body); });
    disco._schedulePoll();
  });
};

DiscoveryClient.prototype.dropServer = function (server) {
  this.logger.log('info', 'Dropping discovery server %s', server);
  this.servers.splice(this.servers.indexOf(server), 1);
};

/* Lookup a service by service type!
 * Accepts one of:
 *   serviceType as string
 *   serviceType:feature as string
 *   predicate function over announcement object
 */
DiscoveryClient.prototype.findAll = function (predicate) {
  var disco = this;
  var candidates = [];

  if (typeof(predicate) != "function") {
    var serviceType = predicate;
    predicate = function(a) {
      return a.serviceType == serviceType || a.serviceType + ":" + a.feature == serviceType;
    };
  }

  Object.keys(disco.state.announcements).forEach(function (id) {
    var a = disco.state.announcements[id];
    if (predicate(a)) {
      candidates.push(a.serviceUri);
    }
  });

  return candidates;
};

DiscoveryClient.prototype.find = function (predicate) {
  var candidates = this.findAll(predicate);
  if (candidates.length === 0) {
    return undefined;
  }
  return candidates[Math.floor(Math.random()*candidates.length)];
};

DiscoveryClient.prototype._announce = function() {
  var disco = this;
  function cb(error, announcement) {
    if (error) {
      disco.errorHandlers.forEach(function (h) { h(error); });
    }
  }
  this.announcements.forEach(function (a) {
    disco._singleAnnounce(a, cb);
  });
};

DiscoveryClient.prototype._singleAnnounce = function (announcement, cb) {
  var server = this._randomServer();
  if (!server) {
    this.logger.log('info', 'Cannot announce. No discovery servers available');
    cb(new Error('Cannot announce. No discovery servers available'));
    return;
  }
  this.logger.log('info', 'Announcing %s on %s', JSON.stringify(announcement), server);
  request({
    url: server + "/announcement",
    method: "POST",
    json: true,
    body: announcement
  }, function (error, response, body) {
    if (error) {
      cb(error);
      return;
    }
    if (response.statusCode != 201) {
      cb(new Error("During announce, bad status code " + response.statusCode + ": " + JSON.stringify(body)));
      return;
    }
    cb(undefined, body);
  });
};

/* Announce ourselves to the registry */
DiscoveryClient.prototype.announce = function (announcement, cb) {
  var disco = this;
  this._singleAnnounce(announcement, function(error, a) {
    if (error) {
      cb(error);
      return;
    }
    disco.logger.log("Announced as " + JSON.stringify(a));
    disco.announcements.push(a);
    cb(undefined, a);
  });
};

/* Remove a previous announcement.  The passed object *must* be the
 * lease as returned by the 'announce' callback. */
DiscoveryClient.prototype.unannounce = function (announcement, callback) {
  var disco = this;
  var server = disco._randomServer();
  var url = server + "/announcement/" + announcement.announcementId;
  disco.announcements.splice(disco.announcements.indexOf(announcement), 1);
  request({
    url: url,
    method: "DELETE"
  }, function (error, response, body) {
    if (error) {
      disco.logger.error(error);
    } else {
      disco.logger.log("Unannounce DELETE '" + url + "' returned " + response.statusCode + ": " + JSON.stringify(body));
    }
    if (callback) {
      callback();
    }
  });
};

module.exports = DiscoveryClient;
<|endoftext|>


<commit_before>'use strict';

const skygear = require('skygear');
skygear.endPoint = 'http://localhost:3001/';
skygear.configApiKey('my_skygear_key');

const Conversation = skygear.Record.extend('conversation');
const ChatUser = skygear.Record.extend('chat_user');

const container = function() {};

container._skygear = skygear;

container.getChatUser = function() {
  const query = new this._skygear.Query(ChatUser);
  query.equalTo('_owner_id', this._skygear.currentUser.id);

  return this._skygear.publicDB.query(query).then(function(records) {
    if (records.length > 0) {
      return records[0];
    }
  }).then(function(record) {
    if (record === null) {
      const user = new ChatUser();
      return this._skygear.publicDB.save(user);
    }
    return record;
  });
};

container.createConversation = function(
                          participant_ids,
                          is_direct_message,
                          distinct,
                          title,
                          metadata) {
  const query = new this._skygear.Query(ChatUser);
  query.contains('_owner_id', participant_ids);

  return this._skygear.publicDB.query(query).then(function(records) {
    if (records.length > 0) {
      return records;
    }
    throw new Error('no user found');
  }).then(function(participants) {
    const conversation = new Conversation();
    conversation.is_direct_message = is_direct_message;
    conversation.title = title;
    conversation.metadata = metadata;

    conversation.participants = participants.map(function(participant) {
      return new this._skygear.Reference(participant);
    });

    return this._skygear.publicDB.save(conversation);
  });
};

module.exports = container;
<commit_msg>add get and and delete conversation<commit_after>'use strict';

const skygear = require('skygear');
skygear.endPoint = 'http://localhost:3001/';
skygear.configApiKey('my_skygear_key');

const Conversation = skygear.Record.extend('conversation');
const ChatUser = skygear.Record.extend('chat_user');

const container = function() {};

container._skygear = skygear;

container.getChatUser = function() {
  const query = new this._skygear.Query(ChatUser);
  query.equalTo('_owner_id', this._skygear.currentUser.id);

  return this._skygear.publicDB.query(query).then(function(records) {
    if (records.length > 0) {
      return records[0];
    }
  }).then(function(record) {
    if (record === null) {
      const user = new ChatUser();
      return this._skygear.publicDB.save(user);
    }
    return record;
  });
};

container.createConversation = function(
                          participant_ids,
                          is_direct_message,
                          distinct,
                          title,
                          metadata) {
  const _this = this;
  const query = new _this._skygear.Query(ChatUser);
  query.contains('_owner_id', participant_ids);

  return _this._skygear.publicDB.query(query).then(function(records) {
    if (records.length > 0) {
      return records;
    }
    throw new Error('no user found');
  }).then(function(participants) {
    const conversation = new Conversation();
    conversation.is_direct_message = is_direct_message;
    conversation.title = title;
    conversation.metadata = metadata;
    conversation.participant_ids = participant_ids;
    return _this._skygear.publicDB.save(conversation);
  });
};

container.getConversation = function(conversation_id) {
  const query = this._skygear.Query(Conversation);
  query.equalTo('_id', conversation_id);
  return this._skygear.publicDB.query(query).then(function(records) {
    if (records.length > 0) {
      return records[0];
    }
    throw new Error('no conversation found');
  });
};

container.getConversations = function() {
  const query = this._skygear.Query(Conversation);
  query.containsValue('participant_ids', this._skygear.currentUser.id);
  return this._skygear.publicDB.query(query)
};

container.deleteConversation = function(conversation_id) {
  const _this = this;
  return _this.getConversation(conversation_id).then(function(conversation) {
    return _this._skygear.publicDB.del(conversation);
  });
}

module.exports = container;
<|endoftext|>


<commit_before>var Renderer = require('./renderer');
var torque = require('torque.js');
var Canvas = require('canvas');
var _ = require('underscore');
var carto = require('carto');

function PngRenderer(layer, sql, attrs) {
    Renderer.apply(this, arguments);

    var cartoCssOptions = torque.common.TorqueLayer.optionsFromCartoCSS(layer.options.cartocss);

    this.provider = new torque.providers.windshaft(_.extend({ no_fetch_map: true }, cartoCssOptions));
    this.rendererOptions = _.extend({}, layer.options, cartoCssOptions, {
        canvasClass: Canvas,
        imageClass: Canvas.Image
    });

    this.step = layer.options.step || 0;
    var shader = new carto.RendererJS().render(layer.options.cartocss);
    this.stepOffset = Math.max.apply(Math, shader.getLayers().map(function(layer) {
        return layer.shader.frames.length;
    }));

    // keep it simple for now and render last step if requested step is bigger than maximum
    if (this.step >= cartoCssOptions.steps) {
        this.step = cartoCssOptions.steps - 1;
    }
}

PngRenderer.prototype = new Renderer();
PngRenderer.prototype.constructor = PngRenderer;

module.exports = PngRenderer;

PngRenderer.prototype.getTile = function(z, x, y, callback) {
    var self = this;
    var attrs = _.extend({stepSelect: this.step, stepOffset: this.stepOffset}, this.attrs);
    this.getTileData(this.sql, {x: x, y: y}, z, this.layer.options.sql, attrs, function(err, rows) {

        var canvas = new Canvas(self.tile_size, self.tile_size);
        var pointRenderer = new torque.renderer.Point(canvas, self.rendererOptions);

        try {
            pointRenderer.renderTile(self.provider.proccessTile(rows, {x: x, y: y}, z), self.step);
        } catch (err) {
            callback(err, null, {});
        }

        canvas.toBuffer(function(err, buf) {
            callback(err, buf, {'Content-Type': 'image/png'});
        });
    });
};
<commit_msg>Adds qualifyURL function as option for the Point renderer For now it is just an identity function<commit_after>var Renderer = require('./renderer');
var torque = require('torque.js');
var Canvas = require('canvas');
var _ = require('underscore');
var carto = require('carto');

function PngRenderer(layer, sql, attrs) {
    Renderer.apply(this, arguments);

    var cartoCssOptions = torque.common.TorqueLayer.optionsFromCartoCSS(layer.options.cartocss);

    this.provider = new torque.providers.windshaft(_.extend({ no_fetch_map: true }, cartoCssOptions));
    this.rendererOptions = _.extend({}, layer.options, cartoCssOptions, {
        canvasClass: Canvas,
        imageClass: Canvas.Image,
        qualifyURL: function(url) {
            return url;
        }
    });

    this.step = layer.options.step || 0;
    var shader = new carto.RendererJS().render(layer.options.cartocss);
    this.stepOffset = Math.max.apply(Math, shader.getLayers().map(function(layer) {
        return layer.shader.frames.length;
    }));

    // keep it simple for now and render last step if requested step is bigger than maximum
    if (this.step >= cartoCssOptions.steps) {
        this.step = cartoCssOptions.steps - 1;
    }
}

PngRenderer.prototype = new Renderer();
PngRenderer.prototype.constructor = PngRenderer;

module.exports = PngRenderer;

PngRenderer.prototype.getTile = function(z, x, y, callback) {
    var self = this;
    var attrs = _.extend({stepSelect: this.step, stepOffset: this.stepOffset}, this.attrs);
    this.getTileData(this.sql, {x: x, y: y}, z, this.layer.options.sql, attrs, function(err, rows) {

        var canvas = new Canvas(self.tile_size, self.tile_size);
        var pointRenderer = new torque.renderer.Point(canvas, self.rendererOptions);

        try {
            pointRenderer.renderTile(self.provider.proccessTile(rows, {x: x, y: y}, z), self.step);
        } catch (err) {
            callback(err, null, {});
        }

        canvas.toBuffer(function(err, buf) {
            callback(err, buf, {'Content-Type': 'image/png'});
        });
    });
};
<|endoftext|>


<commit_before>"use strict";var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(n){return typeof n}:function(n){return n&&"function"==typeof Symbol&&n.constructor===Symbol?"symbol":typeof n};!function(n,t){"undefined"!=typeof module?module.exports=t():"function"==typeof define&&"object"==_typeof(define.amd)?define(t):this[n]=t()}("qoob",function(){return{first:function(n){var t=this.find(n);return t.length>0?t[0]:null},find:function(n){return null===n?null:n.constructor===Array||!0===this.isNodeList(n)?n:"object"===("undefined"==typeof n?"undefined":_typeof(n))?[n]:document.querySelectorAll(n)},isNodeList:function(n){return"undefined"!=typeof n.length&&"undefined"!=typeof n.item},on:function(n,t,e){this.each(n,function(n,i){n.addEventListener?n.addEventListener(t,e):n.attachEvent("on"+t,function(){e.call(n)})})},fadeIn:function(n){},fadeOut:function(n){},hide:function(n){this.each(n,function(n,t){n.style.display="none"})},show:function(n){this.each(n,function(n,t){n.style.display=""})},html:function n(t){var e=arguments.length<=1||void 0===arguments[1]?null:arguments[1],n=[];if(this.each(t,function(t,i){null===e?n.unshift(t.innerHTML):t.innerHTML=e}),null===e)return this._allOrFirstInArray(n)},css:function(n){var t=arguments.length<=1||void 0===arguments[1]?{}:arguments[1];this.each(n,function(n,e){for(var i in t)n.style[i]=t[i]})},addClass:function(n,t){this.each(n,function(n,e){n.classList?n.classList.add(t):n.className+=" "+t})},removeClass:function(n,t){this.each(n,function(n,e){n.classList?n.classList.remove(t):n.className=n.className.replace(new RegExp("(^|\\b)"+t.split(" ").join("|")+"(\\b|$)","gi")," ")})},hasClass:function(n,t){var e=!1;return this.each(n,function(n,i){e=n.classList?n.classList.contains(t)===!0||e:new RegExp("(^| )"+t+"( |$)","gi").test(n.className)===!0||e}),e},children:function n(t){var e=arguments.length<=1||void 0===arguments[1]?null:arguments[1],n=[],i=this;return this.each(t,function(t,o){if(null!==e){var u=t.querySelectorAll(e);i.each(u,function(t,e){n.unshift(t)})}else i.each(t.children,function(t,e){8!=t.nodeType&&n.unshift(t)})}),n},attr:function n(t,e){var i=arguments.length<=2||void 0===arguments[2]?null:arguments[2],n=[];if(this.each(t,function(t,o){if(null===i){var u=t.getAttribute(e);n.unshift(""!==u?u:null)}else t.setAttribute(e,i)}),null===i)return this._allOrFirstInArray(n)},val:function(n){arguments.length<=1||void 0===arguments[1]?null:arguments[1]},text:function(n){},each:function(n,t){for(var e=this.find(n),i=0;i<e.length;i++)t(e[i],i)},documentReady:function(n){"loading"!=document.readyState?n():document.addEventListener?document.addEventListener("DOMContentLoaded",n):document.attachEvent("onreadystatechange",function(){"loading"!=document.readyState&&n()})},_allOrFirstInArray:function(n){return n.length>1?n:n[0]}}});<commit_msg>Build dist.<commit_after>"use strict";var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(n){return typeof n}:function(n){return n&&"function"==typeof Symbol&&n.constructor===Symbol?"symbol":typeof n};!function(n,t){"undefined"!=typeof module?module.exports=t():"function"==typeof define&&"object"==_typeof(define.amd)?define(t):this[n]=t()}("qoob",function(){return{first:function(n){var t=this.find(n);return t.length>0?t[0]:null},find:function(n){return null===n?null:n.constructor===Array||!0===this.isNodeList(n)?n:"object"===("undefined"==typeof n?"undefined":_typeof(n))?[n]:document.querySelectorAll(n)},isNodeList:function(n){return"undefined"!=typeof n.length&&"undefined"!=typeof n.item},on:function(n,t,e){this.each(n,function(n,i){n.addEventListener?n.addEventListener(t,e):n.attachEvent("on"+t,function(){e.call(n)})})},fadeIn:function(n){},fadeOut:function(n){},hide:function(n){this.each(n,function(n,t){n.style.display="none"})},show:function(n){this.each(n,function(n,t){n.style.display=""})},html:function n(t){var e=arguments.length<=1||void 0===arguments[1]?null:arguments[1],n=[];if(this.each(t,function(t,i){null===e?n.unshift(t.innerHTML):t.innerHTML=e}),null===e)return this._allOrFirstInArray(n)},css:function(n){var t=arguments.length<=1||void 0===arguments[1]?{}:arguments[1];this.each(n,function(n,e){for(var i in t)n.style[i]=t[i]})},addClass:function(n,t){this.each(n,function(n,e){n.classList?n.classList.add(t):n.className+=" "+t})},removeClass:function(n,t){this.each(n,function(n,e){n.classList?n.classList.remove(t):n.className=n.className.replace(new RegExp("(^|\\b)"+t.split(" ").join("|")+"(\\b|$)","gi")," ")})},hasClass:function(n,t){var e=!1;return this.each(n,function(n,i){e=n.classList?n.classList.contains(t)===!0||e:new RegExp("(^| )"+t+"( |$)","gi").test(n.className)===!0||e}),e},children:function n(t){var e=arguments.length<=1||void 0===arguments[1]?null:arguments[1],n=[],i=this;return this.each(t,function(t,o){if(null!==e){var u=t.querySelectorAll(e);i.each(u,function(t,e){n.unshift(t)})}else i.each(t.children,function(t,e){8!=t.nodeType&&n.unshift(t)})}),n},attr:function n(t,e){var i=arguments.length<=2||void 0===arguments[2]?null:arguments[2],n=[];if(this.each(t,function(t,o){if(null===i){var u=t.getAttribute(e);n.unshift(""!==u?u:null)}else t.setAttribute(e,i)}),null===i)return this._allOrFirstInArray(n)},val:function n(t){var e=arguments.length<=1||void 0===arguments[1]?null:arguments[1],n=[];if(this.each(t,function(t,i){if(null===e){var o=t.value;n.unshift(""!==o?o:null)}else t.value=e}),null===e)return this._allOrFirstInArray(n)},text:function(n){},each:function(n,t){for(var e=this.find(n),i=0;i<e.length;i++)t(e[i],i)},documentReady:function(n){"loading"!=document.readyState?n():document.addEventListener?document.addEventListener("DOMContentLoaded",n):document.attachEvent("onreadystatechange",function(){"loading"!=document.readyState&&n()})},_allOrFirstInArray:function(n){return n.length>1?n:n[0]}}});<|endoftext|>


<commit_before>//Angela Fan

var gene_name = "CKS1B";

//find the files the gene is in
d3.csv("/Documents/Bradner_work/hockey-sticks/lookup_table.csv", function(error, data){
	//MM1S_H3K27AC_DMSO_HOCKEY.csv
	var input = gene_name;

	var data_length = data.length;

	var file_name_array = [];

	for (var i = 0; i < data.length; i++) {
		//console.log("hello")
		if (data[i].gene == input) {
			if (data[i].file) {

				file_name_array.push(String(data[i].file).split(";"))

				//file_name_array.push(data[i].file);
				//console.log(data[i].file)
				break;
			}
		}
	}

	var ranking_array = [];

	for (var i = 0; i < file_name_array[0].length; i++) {

		var current_file = file_name_array[0][i];

		// console.log(i)

		// console.log(current_file)

		//console.log(current_file)

		plot(current_file, i)

		function plot(current_file, number) {
			d3.csv("/Documents/Bradner_work/hockey-sticks/" + current_file, function(error, current_data) {

				// console.log(current_file)

				// console.log(i)

				current_data.forEach(function(d) {

					var genes = d.PROXIMAL_GENES.split(";");
					// if (i==1) {
					// 	console.log(genes)
					// }
					for (var j = 0; j < genes.length; j++) {
						if (i == 1) {
							//console.log(genes[j])
						}
						if (genes[j] == gene_name) {

							//console.log(current_file)

							ranking_array.push({"filename": current_file, "rank": +d.RANK, "prox_genes": d.PROXIMAL_GENES,
								"super": +d.IS_SUPER, "signal": +d.SIGNAL, "top_gene": d.TOP_GENE, "function": d.PROXIMAL_FUNCTION,
								"start": +d.START, "stop": +d.STOP, "chromosome": d.CHROM});

							//console.log(ranking_array)

							var current_file_name = current_file.split("_HOCKEY")[0];

							//console.log(d)

							current_file_name = "/Documents/Bradner_work/hockey-sticks/" + current_file_name + "_plots/" + "SE_plots_" + current_file_name + "_" + d.REGION_ID + ".pdf";

							$("#pdf_window")
								.append('
 <object data=' + current_file_name +
								' type="application/pdf" width="840px" height="580"> alt : test.pdf </object>
' )

						}
					}

				});

				//console.log(ranking_array)

				// console.log(number)
				// console.log(file_name_array[0].length)

				if (number == 0) {

					tip = d3.tip().attr('class', 'd3-tip');

					var margin = {top: 50, right: 50, bottom: 50, left: 100},
					    width = 1200 - margin.left - margin.right,
					    height = 300 - margin.top - margin.bottom;

					var x = d3.scale.ordinal()
					    .rangeRoundBands([0, width], .1);

					var y = d3.scale.linear()
					    .range([height, 0]);

					var xAxis = d3.svg.axis()
					    .scale(x)
					    .orient("bottom")
					    .tickFormat("");

					var yAxis = d3.svg.axis()
					    .scale(y)
					    .orient("left")
					    .outerTickSize([0]);

					var svg = d3.select("body").append("svg")
					    .attr("width", width + margin.left + margin.right)
					    .attr("height", height + margin.top + margin.bottom)
					  .append("g")
					    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

					svg.call(tip)

					x.domain([0, ranking_array.length]);
					y.domain([0, d3.max(ranking_array, function(d) { return d.rank; })]);

					svg.append("g")
				      	.attr("class", "x axis")
				      	.attr("transform", "translate(0," + height + ")")
				      	.call(xAxis)
				      	.append("text")
				      	.attr("x", 1000)
				      	.attr("y", 15)
				      	.style("text-anchor", "middle")
				      	.text("Files");

					svg.append("g")
				      	.attr("class", "y axis")
				      	.call(yAxis)
				    	.append("text")
				      	.attr("transform", "rotate(-90)")
				      	.attr("dy", "-3.71em")
				      	.style("text-anchor", "end")
				      	.text("Super-enhancer rank");

					svg.selectAll(".bar")
					    .data(ranking_array)
					    .enter().append("rect")
					    .attr("class", "bar")
					    .attr("x", function(d, i) {
					    	return x(i)
					    })
					    .attr("width", x.rangeBand()-2)
					    .attr("y", function(d) { return y(d.rank); })
					    .attr("height", function(d) { return height - y(d.rank); })
					    .on("mouseover", function(d) {

					    	console.log(d)

					    	tip.html("File: " + d.filename + "
Rank: " + d.rank)

					    	return tip.show(d)

					    })
					    .on("mouseout", function(d) {
					    	return tip.hide(d)
					    });

					function type(d) {
					  d.frequency = +d.rank;
					  return d;
					}

					d3.select("input#by_rank").on("change", change_rank);

					function change_rank() {

						// Copy-on-write since tweens are evaluated after a delay.
						var x0 = x.domain(data.sort(this.checked
						    ? function(a, b) { return b.rank - a.rank; }
						    : function(a, b) { return d3.ascending(a.file, b.file); })
						    .map(function(d) { return d.file; }))
						    .copy();

						var transition = svg.transition().duration(750),
						    delay = function(d, i) { return i * 50; };

						transition.selectAll(".bar")
						    .delay(delay)
						    .attr("x", function(d) { return x0(d.file); });

						transition.select(".x.axis")
						    .call(xAxis)
						  .selectAll("g")
						    .delay(delay);

					}

				}

			})
		}

	}

});

<commit_msg>debugged x axis position incorrect<commit_after>//Angela Fan

var gene_name = "CKS1B";

//find the files the gene is in
d3.csv("/Documents/Bradner_work/hockey-sticks/lookup_table.csv", function(error, data){
	//MM1S_H3K27AC_DMSO_HOCKEY.csv
	var input = gene_name;

	var data_length = data.length;

	var file_name_array = [];

	for (var i = 0; i < data.length; i++) {
		//console.log("hello")
		if (data[i].gene == input) {
			if (data[i].file) {

				file_name_array.push(String(data[i].file).split(";"))

				//file_name_array.push(data[i].file);
				//console.log(data[i].file)
				break;
			}
		}
	}

	var ranking_array = [];

	for (var i = 0; i < file_name_array[0].length; i++) {

		var current_file = file_name_array[0][i];

		// console.log(i)

		// console.log(current_file)

		//console.log(current_file)

		plot(current_file, i)

		function plot(current_file, number) {
			d3.csv("/Documents/Bradner_work/hockey-sticks/" + current_file, function(error, current_data) {

				// console.log(current_file)

				// console.log(i)

				current_data.forEach(function(d) {

					var genes = d.PROXIMAL_GENES.split(";");
					// if (i==1) {
					// 	console.log(genes)
					// }
					for (var j = 0; j < genes.length; j++) {
						if (i == 1) {
							//console.log(genes[j])
						}
						if (genes[j] == gene_name) {

							//console.log(current_file)

							ranking_array.push({"filename": current_file, "rank": +d.RANK, "prox_genes": d.PROXIMAL_GENES,
								"super": +d.IS_SUPER, "signal": +d.SIGNAL, "top_gene": d.TOP_GENE, "function": d.PROXIMAL_FUNCTION,
								"start": +d.START, "stop": +d.STOP, "chromosome": d.CHROM});

							//console.log(ranking_array)

							var current_file_name = current_file.split("_HOCKEY")[0];

							//console.log(d)

							current_file_name = "/Documents/Bradner_work/hockey-sticks/" + current_file_name + "_plots/" + "SE_plots_" + current_file_name + "_" + d.REGION_ID + ".pdf";

							$("#pdf_window")
								.append('
 <object data=' + current_file_name +
								' type="application/pdf" width="840px" height="580"> alt : test.pdf </object>
' )

						}
					}

				});

				//console.log(ranking_array)

				// console.log(number)
				// console.log(file_name_array[0].length)

				if (number == 0) {

					tip = d3.tip().attr('class', 'd3-tip');

					var margin = {top: 50, right: 50, bottom: 50, left: 100},
					    width = 1200 - margin.left - margin.right,
					    height = 300 - margin.top - margin.bottom;

					var x = d3.scale.ordinal()
					    .rangeRoundBands([0, width], .1);

					var y = d3.scale.linear()
					    .range([height, 0]);

					var xAxis = d3.svg.axis()
					    .scale(x)
					    .orient("bottom")
					    .tickFormat("");

					var yAxis = d3.svg.axis()
					    .scale(y)
					    .orient("left")
					    .outerTickSize([0]);

					var svg = d3.select("body").append("svg")
					    .attr("width", width + margin.left + margin.right)
					    .attr("height", height + margin.top + margin.bottom)
					  .append("g")
					    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

					svg.call(tip)

					x.domain([0, 1, 2]);
					y.domain(d3.extent(ranking_array, function(d) { return d.rank; }));

					console.log(x.domain())
					console.log(x.range())

					svg.append("g")
				      	.attr("class", "x axis")
				      	.attr("transform", "translate(0," + height + ")")
				      	.call(xAxis)
				      	.append("text")
				      	.attr("x", 1000)
				      	.attr("y", 15)
				      	.style("text-anchor", "middle")
				      	.text("Files");

					svg.append("g")
				      	.attr("class", "y axis")
				      	.call(yAxis)
				    	.append("text")
				      	.attr("transform", "rotate(-90)")
				      	.attr("dy", "-3.71em")
				      	.style("text-anchor", "end")
				      	.text("Super-enhancer rank");

					svg.selectAll(".bar")
					    .data(ranking_array)
					    .enter().append("rect")
					    .attr("class", "bar")
					    .attr("x", function(d, j) {
					    	console.log(j)
					    	console.log(x(j))
					    	return x(j)
					    })
					    .attr("width", x.rangeBand())
					    .attr("y", function(d) { return y(d.rank); })
					    .attr("height", function(d) { return height - y(d.rank); })
					    .on("mouseover", function(d) {

					    	//console.log(d)

					    	tip.html("File: " + d.filename + "
Rank: " + d.rank)

					    	return tip.show(d)

					    })
					    .on("mouseout", function(d) {
					    	return tip.hide(d)
					    });

					function type(d) {
					  d.frequency = +d.rank;
					  return d;
					}

					d3.select("input#by_rank").on("change", change_rank);

					function change_rank() {

						// Copy-on-write since tweens are evaluated after a delay.
						var x0 = x.domain(data.sort(this.checked
						    ? function(a, b) { return b.rank - a.rank; }
						    : function(a, b) { return d3.ascending(a.file, b.file); })
						    .map(function(d) { return d.file; }))
						    .copy();

						var transition = svg.transition().duration(750),
						    delay = function(d, i) { return i * 50; };

						transition.selectAll(".bar")
						    .delay(delay)
						    .attr("x", function(d) { return x0(d.file); });

						transition.select(".x.axis")
						    .call(xAxis)
						  .selectAll("g")
						    .delay(delay);

					}

				}

			})
		}

	}

});

<|endoftext|>


<commit_before>import {Map} from 'immutable';
import {createReducer} from 'redux-immutablejs';

const SET_CACHE_PAGE_ENTRIES = 'garchen-panel/cache/SET_CACHE_PAGE_ENTRIES';
const DELETE_CACHE_PAGE_ENTRIES = 'garchen-panel/cache/DELETE_CACHE_PAGE_ENTRIES';
const SET_CACHE_PAGE_CROSS_FOLDER_SEARCH = 'garchen-panel/cache/SET_CACHE_PAGE_CROSS_FOLDER_SEARCH';
const CLEAR_CACHE_PAGE_ENTRIES = 'garchn-panel/cache/CLEAR_CACHE_PAGE_ENTRIES';
const SET_CACHE_PAGE_FOLDERS = 'garchen-panel/cache/SET_CACHE_PAGE_FOLDERS';

const initialState = Map({
  cachePageEntriesDataSet: {},
  cachePageCrossFolderSearch: {},
  cachePageFolders: {}
});

export default createReducer(initialState, {

  [SET_CACHE_PAGE_ENTRIES]: (state, action) => {
    const cacheDataSet = state.get('cachePageEntriesDataSet');
    cacheDataSet[action.folderId] = action.data;
    return state.set('cachePageEntriesDataSet', cacheDataSet);
  },

  [DELETE_CACHE_PAGE_ENTRIES]: (state, action) => {
    const cacheDataSet = state.get('cachePageEntriesDataSet');
    delete cacheDataSet[action.folderId];
    return state.set('cachePageEntriesDataSet', cacheDataSet);
  },

  [CLEAR_CACHE_PAGE_ENTRIES]: (state) => {
    return state.set('cachePageEntriesDataSet', {});
  },

  [SET_CACHE_PAGE_CROSS_FOLDER_SEARCH]: (state, action) => {
    return state.set('cachePageCrossFolderSearch', action.data);
  },

  [SET_CACHE_PAGE_FOLDERS]: (state, action) => {
    return state.set('cachePageFolders', action.data);
  }
});

export function setCachePageEntries(folderId, data) {
  return {
    type: SET_CACHE_PAGE_ENTRIES,
    folderId,
    data
  };
}

export function deleteCachePageEntries(folderId) {
  return {
    type: DELETE_CACHE_PAGE_ENTRIES,
    folderId
  };
}

export function clearCachePageEntries() {
  return {
    type: CLEAR_CACHE_PAGE_ENTRIES
  };
}

export function setCachePageCrossFolderSearch(data) {
  return {
    type: SET_CACHE_PAGE_CROSS_FOLDER_SEARCH,
    data
  };
}

export function setCachePageFolders(data) {
  return {
    type: SET_CACHE_PAGE_FOLDERS,
    data
  };
}
<commit_msg>add function clearCachePageFolders to cache duck<commit_after>import {Map} from 'immutable';
import {createReducer} from 'redux-immutablejs';

const SET_CACHE_PAGE_ENTRIES = 'garchen-panel/cache/SET_CACHE_PAGE_ENTRIES';
const DELETE_CACHE_PAGE_ENTRIES = 'garchen-panel/cache/DELETE_CACHE_PAGE_ENTRIES';
const SET_CACHE_PAGE_CROSS_FOLDER_SEARCH = 'garchen-panel/cache/SET_CACHE_PAGE_CROSS_FOLDER_SEARCH';
const CLEAR_CACHE_PAGE_ENTRIES = 'garchn-panel/cache/CLEAR_CACHE_PAGE_ENTRIES';
const SET_CACHE_PAGE_FOLDERS = 'garchen-panel/cache/SET_CACHE_PAGE_FOLDERS';
const CLEAR_CACHE_PAGE_FOLDERS = 'garchen-panel/cache/CLEAR_CACHE_PAGE_FOLDERS';

const initialState = Map({
  cachePageEntriesDataSet: {},
  cachePageCrossFolderSearch: {},
  cachePageFolders: {}
});

export default createReducer(initialState, {

  [SET_CACHE_PAGE_ENTRIES]: (state, action) => {
    const cacheDataSet = state.get('cachePageEntriesDataSet');
    cacheDataSet[action.folderId] = action.data;
    return state.set('cachePageEntriesDataSet', cacheDataSet);
  },

  [DELETE_CACHE_PAGE_ENTRIES]: (state, action) => {
    const cacheDataSet = state.get('cachePageEntriesDataSet');
    delete cacheDataSet[action.folderId];
    return state.set('cachePageEntriesDataSet', cacheDataSet);
  },

  [CLEAR_CACHE_PAGE_ENTRIES]: (state) => {
    return state.set('cachePageEntriesDataSet', {});
  },

  [SET_CACHE_PAGE_CROSS_FOLDER_SEARCH]: (state, action) => {
    return state.set('cachePageCrossFolderSearch', action.data);
  },

  [SET_CACHE_PAGE_FOLDERS]: (state, action) => {
    return state.set('cachePageFolders', action.data);
  },

  [CLEAR_CACHE_PAGE_FOLDERS]: (state) => {
    return state.set('cachePageFolders', {});
  }
});

export function setCachePageEntries(folderId, data) {
  return {
    type: SET_CACHE_PAGE_ENTRIES,
    folderId,
    data
  };
}

export function deleteCachePageEntries(folderId) {
  return {
    type: DELETE_CACHE_PAGE_ENTRIES,
    folderId
  };
}

export function clearCachePageEntries() {
  return {
    type: CLEAR_CACHE_PAGE_ENTRIES
  };
}

export function setCachePageCrossFolderSearch(data) {
  return {
    type: SET_CACHE_PAGE_CROSS_FOLDER_SEARCH,
    data
  };
}

export function setCachePageFolders(data) {
  return {
    type: SET_CACHE_PAGE_FOLDERS,
    data
  };
}

export function clearCachePageFolders() {
  return {
    type: CLEAR_CACHE_PAGE_FOLDERS
  };
}
<|endoftext|>


<commit_before>import React from 'react';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import Grid from 'react-bootstrap/lib/Grid';
import s from './About.scss';

const About = () => (
  <Grid className={s.root}>

About Lunch

      Hi! I’m
      {' '}Jeffrey,
      the guy behind Lunch. I created Lunch when my coworkers at
      {' '}Lab Zero
      {' '}realized we had fallen into sort of a rut, food-wise. We kept going to the same few
      places, and our routine was getting stale. So I made Lunch, an easy way to keep a list of
      our favorite restaurants, and decide where to go!

      It’s worked great for us. Every day around 10:30 (we eat early to beat the crowds), we all
      hop on Lunch and vote for whatever looks good. Eventually, a consensus is reached, and we’re
      on our way. That, or a whole bunch of places are tied for votes, and so we just, y’know, talk
      it over like humans.

      Either way, I hope you have fun using Lunch! I think it’s helped us grow closer as coworkers,
      because we’re more invested in trying new places together, and making sure we leave the
      office for a little bit each day. Hot tip: set up a reminder in Slack to vote where to go
      every day!

/remind #general "Vote for lunch! https://lunch.pink" at 10:30am every weekday

Privacy

      I haven’t talked to a lawyer, but I should probably reassure you about what might happen with
      your data when you sign up.

Open-source

      First of all, Lunch is
      {' '}
        open-source and available on GitHub
      . If you’re ever doubtful about what we’re doing with stuff like your email address or
      your team’s daily decisions, you can have a look yourself.

Storing your data

      Public sign-ups for Lunch are currently closed. If you try to log in with your Google account
      and you don’t already have a Lunch account, I’m not going to store any of your data, or keep
      a record that you even tried to log in.

      For those who are already Lunch users, when you link a Google account I only store your email
      address, your name, and your Google profile ID.

Email use

      I only plan on sending email for stuff like password resets and notifications that you’ve
      been added to a new team. I’m certainly not interested in using your email address for any
      reason other than to identify you when you log in. On Lunch, the only people who can see your
      email address are owners of the teams you’re a part of.

Cookies

      Like pretty much any website with a login, Lunch will tell your browser to hold onto a cookie
      that identifies you as being logged in. There’s also a separate “session” cookie that makes
      sure you’re the same person from page to page, so you can see success or failure messages
      when doing things like requesting a password reset.

Cost

      Lunch is currently free. Unlimited users per team, and each user can create or be a part of
      up to three teams. I don’t plan on putting any limitations on what’s currently offered, but I
      might consider charging for future features, whatever those might be. Either way, I haven’t
      even set up a way for you to give me money, so I wouldn’t worry about it.

Advertising

      That said, I’m not against advertisements, or sponsored list items, or something of the sort.
      If I do reach out to restaurants to advertise on Lunch, I’d display tasteful advertisements
      based on their proximity to your team. I wouldn’t give advertisers info like the specific
      names of teams, or the people on it, or anything “personally identifiable” like that. Again,
      this is all just potential stuff in the future, so it’s just a heads up for now.

Be excellent to each other

      Oh and finally, don’t be mean to your teammates. I don’t think there’s any need for global
      moderation on a service this limited, but like, don’t tag restaurants as “disgusting” or
      delete places you don’t like. Just don’t vote for them. Otherwise, your team owner’s totally
      free to kick you out.

      Any other questions or things I forgot to mention? Drop me a line at
      {' '}jeffrey@labzero.com. I’d be happy to hear from
      you.

      I last updated this page on April 11, 2017.

  </Grid>
);

export default withStyles(s)(About);
<commit_msg>Mention invitations after login<commit_after>import React from 'react';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import Grid from 'react-bootstrap/lib/Grid';
import s from './About.scss';

const About = () => (
  <Grid className={s.root}>

About Lunch

      Hi! I’m
      {' '}Jeffrey,
      the guy behind Lunch. I created Lunch when my coworkers at
      {' '}Lab Zero
      {' '}realized we had fallen into sort of a rut, food-wise. We kept going to the same few
      places, and our routine was getting stale. So I made Lunch, an easy way to keep a list of
      our favorite restaurants, and decide where to go!

      It’s worked great for us. Every day around 10:30 (we eat early to beat the crowds), we all
      hop on Lunch and vote for whatever looks good. Eventually, a consensus is reached, and we’re
      on our way. That, or a whole bunch of places are tied for votes, and so we just, y’know, talk
      it over like humans.

      Either way, I hope you have fun using Lunch! I think it’s helped us grow closer as coworkers,
      because we’re more invested in trying new places together, and making sure we leave the
      office for a little bit each day. Hot tip: set up a reminder in Slack to vote where to go
      every day!

/remind #general "Vote for lunch! https://lunch.pink" at 10:30am every weekday

Privacy

      I haven’t talked to a lawyer, but I should probably reassure you about what might happen with
      your data when you sign up.

Open-source

      First of all, Lunch is
      {' '}
        open-source and available on GitHub
      . If you’re ever doubtful about what we’re doing with stuff like your email address or
      your team’s daily decisions, you can have a look yourself.

Storing your data

      Public sign-ups for Lunch are currently closed. If you try to log in with your Google account
      and you don’t already have a Lunch account, I’m not going to store any of your data, or keep
      a record that you even tried to log in — but you will be prompted to sign up for an
      invitation.

      For those who are already Lunch users, when you link a Google account I only store your email
      address, your name, and your Google profile ID.

Email use

      I only plan on sending email for stuff like password resets and notifications that you’ve
      been added to a new team. I’m certainly not interested in using your email address for any
      reason other than to identify you when you log in. On Lunch, the only people who can see your
      email address are owners of the teams you’re a part of.

Cookies

      Like pretty much any website with a login, Lunch will tell your browser to hold onto a cookie
      that identifies you as being logged in. There’s also a separate “session” cookie that makes
      sure you’re the same person from page to page, so you can see success or failure messages
      when doing things like requesting a password reset.

Cost

      Lunch is currently free. Unlimited users per team, and each user can create or be a part of
      up to three teams. I don’t plan on putting any limitations on what’s currently offered, but I
      might consider charging for future features, whatever those might be. Either way, I haven’t
      even set up a way for you to give me money, so I wouldn’t worry about it.

Advertising

      That said, I’m not against advertisements, or sponsored list items, or something of the sort.
      If I do reach out to restaurants to advertise on Lunch, I’d display tasteful advertisements
      based on their proximity to your team. I wouldn’t give advertisers info like the specific
      names of teams, or the people on it, or anything “personally identifiable” like that. Again,
      this is all just potential stuff in the future, so it’s just a heads up for now.

Be excellent to each other

      Oh and finally, don’t be mean to your teammates. I don’t think there’s any need for global
      moderation on a service this limited, but like, don’t tag restaurants as “disgusting” or
      delete places you don’t like. Just don’t vote for them. Otherwise, your team owner’s totally
      free to kick you out.

      Any other questions or things I forgot to mention? Drop me a line at
      {' '}jeffrey@labzero.com. I’d be happy to hear from
      you.

      I last updated this page on May 1, 2017.

  </Grid>
);

export default withStyles(s)(About);
<|endoftext|>