<commit_before>/**
 * Visual Blocks Language
 *
 * Copyright 2012 Google Inc.
 * http://code.google.com/p/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 Procedure blocks for Blockly.
 * @author fraser@google.com (Neil Fraser)
 */
'use strict';

if (!Blockly.Language) Blockly.Language = {};

Blockly.Language.procedures_defnoreturn = {
  // Define a procedure with no return value.
  category: null,  // Procedures are handled specially.
  helpUrl: Blockly.LANG_PROCEDURES_DEFNORETURN_HELPURL,
  init: function() {
    this.setColour(290);
    var name = Blockly.Procedures.findLegalName(
        Blockly.LANG_PROCEDURES_DEFNORETURN_PROCEDURE, this);
    this.appendDummyInput()
        .appendTitle(new Blockly.FieldTextInput(name,
        Blockly.Procedures.rename), 'NAME')
        .appendTitle('', 'PARAMS');
    this.appendStatementInput('STACK')
        .appendTitle(Blockly.LANG_PROCEDURES_DEFNORETURN_DO);
    this.setMutator(new Blockly.Mutator(['procedures_mutatorarg']));
    this.setTooltip(Blockly.LANG_PROCEDURES_DEFNORETURN_TOOLTIP_1);
    this.arguments_ = [];
  },
  updateParams_: function() {
    // Check for duplicated arguments.
    var badArg = false;
    var hash = {};
    for (var x = 0; x < this.arguments_.length; x++) {
      if (hash['arg_' + this.arguments_[x].toLowerCase()]) {
        badArg = true;
        break;
      }
      hash['arg_' + this.arguments_[x].toLowerCase()] = true;
    }
    if (badArg) {
      this.setWarningText(Blockly.LANG_PROCEDURES_DEF_DUPLICATE_WARNING);
    } else {
      this.setWarningText(null);
    }
    // Merge the arguments into a human-readable list.
    var paramString = this.arguments_.join(', ');
    this.setTitleValue(paramString, 'PARAMS');
  },
  mutationToDom: function() {
    var container = document.createElement('mutation');
    for (var x = 0; x < this.arguments_.length; x++) {
      var parameter = document.createElement('arg');
      parameter.setAttribute('name', this.arguments_[x]);
      container.appendChild(parameter);
    }
    return container;
  },
  domToMutation: function(xmlElement) {
    this.arguments_ = [];
    for (var x = 0, childNode; childNode = xmlElement.childNodes[x]; x++) {
      if (childNode.nodeName == 'arg') {
        this.arguments_.push(childNode.getAttribute('name'));
      }
    }
    this.updateParams_();
  },
  decompose: function(workspace) {
    var containerBlock = new Blockly.Block(workspace,
                                           'procedures_mutatorcontainer');
    containerBlock.initSvg();
    var connection = containerBlock.getInput('STACK').connection;
    for (var x = 0; x < this.arguments_.length; x++) {
      var paramBlock = new Blockly.Block(workspace, 'procedures_mutatorarg');
      paramBlock.initSvg();
      paramBlock.setTitleValue(this.arguments_[x], 'NAME');
      // Store the old location.
      paramBlock.oldLocation = x;
      connection.connect(paramBlock.previousConnection);
      connection = paramBlock.nextConnection;
    }
    // Initialize procedure's callers with blank IDs.
    Blockly.Procedures.mutateCallers(this.getTitleValue('NAME'),
                                     this.workspace, this.arguments_, null);
    return containerBlock;
  },
  compose: function(containerBlock) {
    this.arguments_ = [];
    paramIds = [];
    var paramBlock = containerBlock.getInputTargetBlock('STACK');
    while (paramBlock) {
      this.arguments_.push(paramBlock.getTitleValue('NAME'));
      paramIds.push(paramBlock.id);
      paramBlock = paramBlock.nextConnection &&
          paramBlock.nextConnection.targetBlock();
    }
    this.updateParams_();
    Blockly.Procedures.mutateCallers(this.getTitleValue('NAME'),
                                     this.workspace, this.arguments_, paramIds);
  },
  destroy: function() {
    var name = this.getTitleValue('NAME');
    var editable = this.editable;
    var workspace = this.workspace;
    // Call parent's destructor.
    Blockly.Block.prototype.destroy.apply(this, arguments);
    if (this.editable) {
      // Destroy any callers.
      Blockly.Procedures.destroyCallers(name, workspace);
    }
  },
  getProcedureDef: function() {
    // Return the name of the defined procedure,
    // a list of all its arguments,
    // and that it DOES NOT have a return value.
    return [this.getTitleValue('NAME'), this.arguments_, false];
  },
  getVars: function() {
    return this.arguments_;
  },
  renameVar: function(oldName, newName) {
    var change = false;
    for (var x = 0; x < this.arguments_.length; x++) {
      if (Blockly.Names.equals(oldName, this.arguments_[x])) {
        this.arguments_[x] = newName;
        change = true;
      }
    }
    if (change) {
      this.updateParams_();
      // Update the mutator's variables if the mutator is open.
      if (this.mutator.isVisible_()) {
        var blocks = this.mutator.workspace_.getAllBlocks();
        for (var x = 0, block; block = blocks[x]; x++) {
          if (block.type == 'procedures_mutatorarg' &&
              Blockly.Names.equals(oldName, block.getTitleValue('NAME'))) {
            block.setTitleValue(newName, 'NAME');
          }
        }
      }
    }
  }
};

Blockly.Language.procedures_defreturn = {
  // Define a procedure with a return value.
  category: null,  // Procedures are handled specially.
  helpUrl: Blockly.LANG_PROCEDURES_DEFRETURN_HELPURL,
  init: function() {
    this.setColour(290);
    var name = Blockly.Procedures.findLegalName(
        Blockly.LANG_PROCEDURES_DEFRETURN_PROCEDURE, this);
    this.appendDummyInput()
        .appendTitle(new Blockly.FieldTextInput(name,
        Blockly.Procedures.rename), 'NAME')
        .appendTitle('', 'PARAMS');
    this.appendStatementInput('STACK')
        .appendTitle(Blockly.LANG_PROCEDURES_DEFRETURN_DO);
    this.appendValueInput('RETURN')
        .setAlign(Blockly.ALIGN_RIGHT)
        .appendTitle(Blockly.LANG_PROCEDURES_DEFRETURN_RETURN);
    this.setMutator(new Blockly.Mutator(['procedures_mutatorarg']));
    this.setTooltip(Blockly.LANG_PROCEDURES_DEFRETURN_TOOLTIP_1);
    this.arguments_ = [];
  },
  updateParams_: Blockly.Language.procedures_defnoreturn.updateParams_,
  mutationToDom: Blockly.Language.procedures_defnoreturn.mutationToDom,
  domToMutation: Blockly.Language.procedures_defnoreturn.domToMutation,
  decompose: Blockly.Language.procedures_defnoreturn.decompose,
  compose: Blockly.Language.procedures_defnoreturn.compose,
  destroy: Blockly.Language.procedures_defnoreturn.destroy,
  getProcedureDef: function() {
    // Return the name of the defined procedure,
    // a list of all its arguments,
    // and that it DOES have a return value.
    return [this.getTitleValue('NAME'), this.arguments_, true];
  },
  getVars: Blockly.Language.procedures_defnoreturn.getVars,
  renameVar: Blockly.Language.procedures_defnoreturn.renameVar
};

Blockly.Language.procedures_mutatorcontainer = {
  // Procedure container (for mutator dialog).
  init: function() {
    this.setColour(290);
    this.appendDummyInput()
        .appendTitle(Blockly.LANG_PROCEDURES_MUTATORCONTAINER_TITLE);
    this.appendStatementInput('STACK');
    this.setTooltip('');
    this.contextMenu = false;
  }
};

Blockly.Language.procedures_mutatorarg = {
  // Procedure argument (for mutator dialog).
  init: function() {
    this.setColour(290);
    this.appendDummyInput()
        .appendTitle(Blockly.LANG_PROCEDURES_MUTATORARG_TITLE)
        .appendTitle(new Blockly.FieldTextInput('x',
        Blockly.Language.procedures_mutatorarg.validator), 'NAME');
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setTooltip('');
    this.contextMenu = false;
  }
};

Blockly.Language.procedures_mutatorarg.validator = function(newVar) {
  // Merge runs of whitespace.  Strip leading and trailing whitespace.
  // Beyond this, all names are legal.
  newVar = newVar.replace(/[\s\xa0]+/g, ' ').replace(/^ | $/g, '');
  return newVar || null;
};

Blockly.Language.procedures_callnoreturn = {
  // Call a procedure with no return value.
  category: null,  // Procedures are handled specially.
  helpUrl: Blockly.LANG_PROCEDURES_CALLNORETURN_HELPURL,
  init: function() {
    this.setColour(290);
    this.appendDummyInput()
        .appendTitle(Blockly.LANG_PROCEDURES_CALLNORETURN_CALL)
        .appendTitle(Blockly.LANG_PROCEDURES_CALLNORETURN_PROCEDURE, 'NAME');
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setTooltip(Blockly.LANG_PROCEDURES_CALLNORETURN_TOOLTIP_1);
    this.arguments_ = [];
    this.quarkConnections_ = null;
    this.quarkArguments_ = null;
  },
  getProcedureCall: function() {
    return this.getTitleValue('NAME');
  },
  renameProcedure: function(oldName, newName) {
    if (Blockly.Names.equals(oldName, this.getTitleValue('NAME'))) {
      this.setTitleValue(newName, 'NAME');
    }
  },
  setProcedureParameters: function(paramNames, paramIds) {
    if (!paramIds) {
      // Reset the quarks (a mutator is about to open).
      this.quarkConnections_ = {};
      this.quarkArguments_ = null;
      return;
    }
    if (paramIds.length != paramNames.length) {
      throw 'Error: paramNames and paramIds must be the same length.';
    }
    if (!this.quarkArguments_) {
      // Initialize tracking for this block.
      this.quarkConnections_ = {};
      if (paramNames.join('\n') == this.arguments_.join('\n')) {
        // No change to the parameters, allow quarkConnections_ to be
        // populated with the existing connections.
        this.quarkArguments_ = paramIds;
      }
    }
    // Update the quarkConnections_ with existing connections.
    for (var x = 0; x < this.arguments_.length; x++) {
      var connection = this.getInput('ARG' + x).connection.targetConnection;
      this.quarkConnections_[this.quarkArguments_[x]] = connection;
    }
    // Switch off rendering while the block is rebuilt.
    var savedRendered = this.rendered;
    this.rendered = false;
    // Disconnect all argument blocks and destroy all inputs.
    for (var x = this.arguments_.length - 1; x >= 0; x--) {
      this.removeInput('ARG' + x);
    }
    // Rebuild the block's arguments.
    this.arguments_ = [].concat(paramNames);
    this.quarkArguments_ = paramIds;
    for (var x = 0; x < this.arguments_.length; x++) {
      var input = this.appendValueInput('ARG' + x)
          .setAlign(Blockly.ALIGN_RIGHT)
          .appendTitle(this.arguments_[x]);
      if (this.quarkArguments_) {
        // Reconnect any child blocks.
        var quarkName = this.quarkArguments_[x];
        if (quarkName in this.quarkConnections_) {
          var connection = this.quarkConnections_[quarkName];
          if (!connection || connection.targetConnection ||
              connection.sourceBlock_.workspace != this.workspace) {
            // Block no longer exists or has been attached elsewhere.
            delete this.quarkConnections_[quarkName];
          } else {
            input.connection.connect(connection);
          }
        }
      }
    }
    // Restore rendering and show the changes.
    this.rendered = savedRendered;
    if (this.rendered) {
      this.render();
    }
  },
  mutationToDom: function() {
    // Save the name and arguments (none of which are editable).
    var container = document.createElement('mutation');
    container.setAttribute('name', this.getTitleValue('NAME'));
    for (var x = 0; x < this.arguments_.length; x++) {
      var parameter = document.createElement('arg');
      parameter.setAttribute('name', this.arguments_[x]);
      container.appendChild(parameter);
    }
    return container;
  },
  domToMutation: function(xmlElement) {
    // Restore the name and parameters.
    var name = xmlElement.getAttribute('name');
    this.setTitleValue(name, 'NAME');
    this.arguments_ = [];
    for (var x = 0, childNode; childNode = xmlElement.childNodes[x]; x++) {
      if (childNode.tagName && childNode.tagName.toLowerCase() == 'arg') {
        var paramName = childNode.getAttribute('name');
        this.appendValueInput('ARG' + this.arguments_.length)
            .setAlign(Blockly.ALIGN_RIGHT)
            .appendTitle(paramName);
        this.arguments_.push(paramName);
      }
    }
  },
  renameVar: function(oldName, newName) {
    for (var x = 0; x < this.arguments_.length; x++) {
      if (Blockly.Names.equals(oldName, this.arguments_[x])) {
        this.arguments_[x] = newName;
        this.getInput('ARG' + x).titleRow[0].setText(newName);
      }
    }
  },
  customContextMenu: function(options) {
    // Add option to find caller.
    var option = {enabled: true};
    option.text = Blockly.LANG_PROCEDURES_HIGHLIGHT_DEF;
    var name = this.getTitleValue('NAME');
    var workspace = this.workspace;
    option.callback = function() {
      var blocks = workspace.getAllBlocks(false);
      for (var x = 0; x < blocks.length; x++) {
        var func = blocks[x].getProcedureDef;
        if (func) {
          var tuple = func.call(blocks[x]);
          if (tuple && Blockly.Names.equals(tuple[0], name)) {
            blocks[x].select();
            break;
          }
        }
      }
    };
    options.push(option);
  }
};

Blockly.Language.procedures_callreturn = {
  // Call a procedure with a return value.
  category: null,  // Procedures are handled specially.
  helpUrl: Blockly.LANG_PROCEDURES_CALLRETURN_HELPURL,
  init: function() {
    this.setColour(290);
    this.appendDummyInput()
        .appendTitle(Blockly.LANG_PROCEDURES_CALLRETURN_CALL)
        .appendTitle(Blockly.LANG_PROCEDURES_CALLRETURN_PROCEDURE, 'NAME');
    this.setOutput(true, null);
    this.setTooltip(Blockly.LANG_PROCEDURES_CALLRETURN_TOOLTIP_1);
    this.arguments_ = [];
    this.quarkConnections_ = null;
    this.quarkArguments_ = null;
  },
  getProcedureCall: Blockly.Language.procedures_callnoreturn.getProcedureCall,
  renameProcedure: Blockly.Language.procedures_callnoreturn.renameProcedure,
  setProcedureParameters:
      Blockly.Language.procedures_callnoreturn.setProcedureParameters,
  mutationToDom: Blockly.Language.procedures_callnoreturn.mutationToDom,
  domToMutation: Blockly.Language.procedures_callnoreturn.domToMutation,
  renameVar: Blockly.Language.procedures_callnoreturn.renameVar,
  customContextMenu: Blockly.Language.procedures_callnoreturn.customContextMenu
};
<commit_msg>Fix missing var (issue 82).<commit_after>/**
 * Visual Blocks Language
 *
 * Copyright 2012 Google Inc.
 * http://code.google.com/p/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 Procedure blocks for Blockly.
 * @author fraser@google.com (Neil Fraser)
 */
'use strict';

if (!Blockly.Language) Blockly.Language = {};

Blockly.Language.procedures_defnoreturn = {
  // Define a procedure with no return value.
  category: null,  // Procedures are handled specially.
  helpUrl: Blockly.LANG_PROCEDURES_DEFNORETURN_HELPURL,
  init: function() {
    this.setColour(290);
    var name = Blockly.Procedures.findLegalName(
        Blockly.LANG_PROCEDURES_DEFNORETURN_PROCEDURE, this);
    this.appendDummyInput()
        .appendTitle(new Blockly.FieldTextInput(name,
        Blockly.Procedures.rename), 'NAME')
        .appendTitle('', 'PARAMS');
    this.appendStatementInput('STACK')
        .appendTitle(Blockly.LANG_PROCEDURES_DEFNORETURN_DO);
    this.setMutator(new Blockly.Mutator(['procedures_mutatorarg']));
    this.setTooltip(Blockly.LANG_PROCEDURES_DEFNORETURN_TOOLTIP_1);
    this.arguments_ = [];
  },
  updateParams_: function() {
    // Check for duplicated arguments.
    var badArg = false;
    var hash = {};
    for (var x = 0; x < this.arguments_.length; x++) {
      if (hash['arg_' + this.arguments_[x].toLowerCase()]) {
        badArg = true;
        break;
      }
      hash['arg_' + this.arguments_[x].toLowerCase()] = true;
    }
    if (badArg) {
      this.setWarningText(Blockly.LANG_PROCEDURES_DEF_DUPLICATE_WARNING);
    } else {
      this.setWarningText(null);
    }
    // Merge the arguments into a human-readable list.
    var paramString = this.arguments_.join(', ');
    this.setTitleValue(paramString, 'PARAMS');
  },
  mutationToDom: function() {
    var container = document.createElement('mutation');
    for (var x = 0; x < this.arguments_.length; x++) {
      var parameter = document.createElement('arg');
      parameter.setAttribute('name', this.arguments_[x]);
      container.appendChild(parameter);
    }
    return container;
  },
  domToMutation: function(xmlElement) {
    this.arguments_ = [];
    for (var x = 0, childNode; childNode = xmlElement.childNodes[x]; x++) {
      if (childNode.nodeName == 'arg') {
        this.arguments_.push(childNode.getAttribute('name'));
      }
    }
    this.updateParams_();
  },
  decompose: function(workspace) {
    var containerBlock = new Blockly.Block(workspace,
                                           'procedures_mutatorcontainer');
    containerBlock.initSvg();
    var connection = containerBlock.getInput('STACK').connection;
    for (var x = 0; x < this.arguments_.length; x++) {
      var paramBlock = new Blockly.Block(workspace, 'procedures_mutatorarg');
      paramBlock.initSvg();
      paramBlock.setTitleValue(this.arguments_[x], 'NAME');
      // Store the old location.
      paramBlock.oldLocation = x;
      connection.connect(paramBlock.previousConnection);
      connection = paramBlock.nextConnection;
    }
    // Initialize procedure's callers with blank IDs.
    Blockly.Procedures.mutateCallers(this.getTitleValue('NAME'),
                                     this.workspace, this.arguments_, null);
    return containerBlock;
  },
  compose: function(containerBlock) {
    this.arguments_ = [];
    var paramIds = [];
    var paramBlock = containerBlock.getInputTargetBlock('STACK');
    while (paramBlock) {
      this.arguments_.push(paramBlock.getTitleValue('NAME'));
      paramIds.push(paramBlock.id);
      paramBlock = paramBlock.nextConnection &&
          paramBlock.nextConnection.targetBlock();
    }
    this.updateParams_();
    Blockly.Procedures.mutateCallers(this.getTitleValue('NAME'),
                                     this.workspace, this.arguments_, paramIds);
  },
  destroy: function() {
    var name = this.getTitleValue('NAME');
    var editable = this.editable;
    var workspace = this.workspace;
    // Call parent's destructor.
    Blockly.Block.prototype.destroy.apply(this, arguments);
    if (this.editable) {
      // Destroy any callers.
      Blockly.Procedures.destroyCallers(name, workspace);
    }
  },
  getProcedureDef: function() {
    // Return the name of the defined procedure,
    // a list of all its arguments,
    // and that it DOES NOT have a return value.
    return [this.getTitleValue('NAME'), this.arguments_, false];
  },
  getVars: function() {
    return this.arguments_;
  },
  renameVar: function(oldName, newName) {
    var change = false;
    for (var x = 0; x < this.arguments_.length; x++) {
      if (Blockly.Names.equals(oldName, this.arguments_[x])) {
        this.arguments_[x] = newName;
        change = true;
      }
    }
    if (change) {
      this.updateParams_();
      // Update the mutator's variables if the mutator is open.
      if (this.mutator.isVisible_()) {
        var blocks = this.mutator.workspace_.getAllBlocks();
        for (var x = 0, block; block = blocks[x]; x++) {
          if (block.type == 'procedures_mutatorarg' &&
              Blockly.Names.equals(oldName, block.getTitleValue('NAME'))) {
            block.setTitleValue(newName, 'NAME');
          }
        }
      }
    }
  }
};

Blockly.Language.procedures_defreturn = {
  // Define a procedure with a return value.
  category: null,  // Procedures are handled specially.
  helpUrl: Blockly.LANG_PROCEDURES_DEFRETURN_HELPURL,
  init: function() {
    this.setColour(290);
    var name = Blockly.Procedures.findLegalName(
        Blockly.LANG_PROCEDURES_DEFRETURN_PROCEDURE, this);
    this.appendDummyInput()
        .appendTitle(new Blockly.FieldTextInput(name,
        Blockly.Procedures.rename), 'NAME')
        .appendTitle('', 'PARAMS');
    this.appendStatementInput('STACK')
        .appendTitle(Blockly.LANG_PROCEDURES_DEFRETURN_DO);
    this.appendValueInput('RETURN')
        .setAlign(Blockly.ALIGN_RIGHT)
        .appendTitle(Blockly.LANG_PROCEDURES_DEFRETURN_RETURN);
    this.setMutator(new Blockly.Mutator(['procedures_mutatorarg']));
    this.setTooltip(Blockly.LANG_PROCEDURES_DEFRETURN_TOOLTIP_1);
    this.arguments_ = [];
  },
  updateParams_: Blockly.Language.procedures_defnoreturn.updateParams_,
  mutationToDom: Blockly.Language.procedures_defnoreturn.mutationToDom,
  domToMutation: Blockly.Language.procedures_defnoreturn.domToMutation,
  decompose: Blockly.Language.procedures_defnoreturn.decompose,
  compose: Blockly.Language.procedures_defnoreturn.compose,
  destroy: Blockly.Language.procedures_defnoreturn.destroy,
  getProcedureDef: function() {
    // Return the name of the defined procedure,
    // a list of all its arguments,
    // and that it DOES have a return value.
    return [this.getTitleValue('NAME'), this.arguments_, true];
  },
  getVars: Blockly.Language.procedures_defnoreturn.getVars,
  renameVar: Blockly.Language.procedures_defnoreturn.renameVar
};

Blockly.Language.procedures_mutatorcontainer = {
  // Procedure container (for mutator dialog).
  init: function() {
    this.setColour(290);
    this.appendDummyInput()
        .appendTitle(Blockly.LANG_PROCEDURES_MUTATORCONTAINER_TITLE);
    this.appendStatementInput('STACK');
    this.setTooltip('');
    this.contextMenu = false;
  }
};

Blockly.Language.procedures_mutatorarg = {
  // Procedure argument (for mutator dialog).
  init: function() {
    this.setColour(290);
    this.appendDummyInput()
        .appendTitle(Blockly.LANG_PROCEDURES_MUTATORARG_TITLE)
        .appendTitle(new Blockly.FieldTextInput('x',
        Blockly.Language.procedures_mutatorarg.validator), 'NAME');
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setTooltip('');
    this.contextMenu = false;
  }
};

Blockly.Language.procedures_mutatorarg.validator = function(newVar) {
  // Merge runs of whitespace.  Strip leading and trailing whitespace.
  // Beyond this, all names are legal.
  newVar = newVar.replace(/[\s\xa0]+/g, ' ').replace(/^ | $/g, '');
  return newVar || null;
};

Blockly.Language.procedures_callnoreturn = {
  // Call a procedure with no return value.
  category: null,  // Procedures are handled specially.
  helpUrl: Blockly.LANG_PROCEDURES_CALLNORETURN_HELPURL,
  init: function() {
    this.setColour(290);
    this.appendDummyInput()
        .appendTitle(Blockly.LANG_PROCEDURES_CALLNORETURN_CALL)
        .appendTitle(Blockly.LANG_PROCEDURES_CALLNORETURN_PROCEDURE, 'NAME');
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setTooltip(Blockly.LANG_PROCEDURES_CALLNORETURN_TOOLTIP_1);
    this.arguments_ = [];
    this.quarkConnections_ = null;
    this.quarkArguments_ = null;
  },
  getProcedureCall: function() {
    return this.getTitleValue('NAME');
  },
  renameProcedure: function(oldName, newName) {
    if (Blockly.Names.equals(oldName, this.getTitleValue('NAME'))) {
      this.setTitleValue(newName, 'NAME');
    }
  },
  setProcedureParameters: function(paramNames, paramIds) {
    if (!paramIds) {
      // Reset the quarks (a mutator is about to open).
      this.quarkConnections_ = {};
      this.quarkArguments_ = null;
      return;
    }
    if (paramIds.length != paramNames.length) {
      throw 'Error: paramNames and paramIds must be the same length.';
    }
    if (!this.quarkArguments_) {
      // Initialize tracking for this block.
      this.quarkConnections_ = {};
      if (paramNames.join('\n') == this.arguments_.join('\n')) {
        // No change to the parameters, allow quarkConnections_ to be
        // populated with the existing connections.
        this.quarkArguments_ = paramIds;
      }
    }
    // Update the quarkConnections_ with existing connections.
    for (var x = 0; x < this.arguments_.length; x++) {
      var connection = this.getInput('ARG' + x).connection.targetConnection;
      this.quarkConnections_[this.quarkArguments_[x]] = connection;
    }
    // Switch off rendering while the block is rebuilt.
    var savedRendered = this.rendered;
    this.rendered = false;
    // Disconnect all argument blocks and destroy all inputs.
    for (var x = this.arguments_.length - 1; x >= 0; x--) {
      this.removeInput('ARG' + x);
    }
    // Rebuild the block's arguments.
    this.arguments_ = [].concat(paramNames);
    this.quarkArguments_ = paramIds;
    for (var x = 0; x < this.arguments_.length; x++) {
      var input = this.appendValueInput('ARG' + x)
          .setAlign(Blockly.ALIGN_RIGHT)
          .appendTitle(this.arguments_[x]);
      if (this.quarkArguments_) {
        // Reconnect any child blocks.
        var quarkName = this.quarkArguments_[x];
        if (quarkName in this.quarkConnections_) {
          var connection = this.quarkConnections_[quarkName];
          if (!connection || connection.targetConnection ||
              connection.sourceBlock_.workspace != this.workspace) {
            // Block no longer exists or has been attached elsewhere.
            delete this.quarkConnections_[quarkName];
          } else {
            input.connection.connect(connection);
          }
        }
      }
    }
    // Restore rendering and show the changes.
    this.rendered = savedRendered;
    if (this.rendered) {
      this.render();
    }
  },
  mutationToDom: function() {
    // Save the name and arguments (none of which are editable).
    var container = document.createElement('mutation');
    container.setAttribute('name', this.getTitleValue('NAME'));
    for (var x = 0; x < this.arguments_.length; x++) {
      var parameter = document.createElement('arg');
      parameter.setAttribute('name', this.arguments_[x]);
      container.appendChild(parameter);
    }
    return container;
  },
  domToMutation: function(xmlElement) {
    // Restore the name and parameters.
    var name = xmlElement.getAttribute('name');
    this.setTitleValue(name, 'NAME');
    this.arguments_ = [];
    for (var x = 0, childNode; childNode = xmlElement.childNodes[x]; x++) {
      if (childNode.tagName && childNode.tagName.toLowerCase() == 'arg') {
        var paramName = childNode.getAttribute('name');
        this.appendValueInput('ARG' + this.arguments_.length)
            .setAlign(Blockly.ALIGN_RIGHT)
            .appendTitle(paramName);
        this.arguments_.push(paramName);
      }
    }
  },
  renameVar: function(oldName, newName) {
    for (var x = 0; x < this.arguments_.length; x++) {
      if (Blockly.Names.equals(oldName, this.arguments_[x])) {
        this.arguments_[x] = newName;
        this.getInput('ARG' + x).titleRow[0].setText(newName);
      }
    }
  },
  customContextMenu: function(options) {
    // Add option to find caller.
    var option = {enabled: true};
    option.text = Blockly.LANG_PROCEDURES_HIGHLIGHT_DEF;
    var name = this.getTitleValue('NAME');
    var workspace = this.workspace;
    option.callback = function() {
      var blocks = workspace.getAllBlocks(false);
      for (var x = 0; x < blocks.length; x++) {
        var func = blocks[x].getProcedureDef;
        if (func) {
          var tuple = func.call(blocks[x]);
          if (tuple && Blockly.Names.equals(tuple[0], name)) {
            blocks[x].select();
            break;
          }
        }
      }
    };
    options.push(option);
  }
};

Blockly.Language.procedures_callreturn = {
  // Call a procedure with a return value.
  category: null,  // Procedures are handled specially.
  helpUrl: Blockly.LANG_PROCEDURES_CALLRETURN_HELPURL,
  init: function() {
    this.setColour(290);
    this.appendDummyInput()
        .appendTitle(Blockly.LANG_PROCEDURES_CALLRETURN_CALL)
        .appendTitle(Blockly.LANG_PROCEDURES_CALLRETURN_PROCEDURE, 'NAME');
    this.setOutput(true, null);
    this.setTooltip(Blockly.LANG_PROCEDURES_CALLRETURN_TOOLTIP_1);
    this.arguments_ = [];
    this.quarkConnections_ = null;
    this.quarkArguments_ = null;
  },
  getProcedureCall: Blockly.Language.procedures_callnoreturn.getProcedureCall,
  renameProcedure: Blockly.Language.procedures_callnoreturn.renameProcedure,
  setProcedureParameters:
      Blockly.Language.procedures_callnoreturn.setProcedureParameters,
  mutationToDom: Blockly.Language.procedures_callnoreturn.mutationToDom,
  domToMutation: Blockly.Language.procedures_callnoreturn.domToMutation,
  renameVar: Blockly.Language.procedures_callnoreturn.renameVar,
  customContextMenu: Blockly.Language.procedures_callnoreturn.customContextMenu
};
<|endoftext|>


<commit_before>'use strict';

const { COMMANDS } = require('../../../constants');
const { EngineError } = require('../../../error');

// Check output, for the errors that should not happen,
// i.e. server-side (e.g. 500)
// In short: response should be an array of objects
const validateResponse = function ({ command, response }) {
  const { multiple } = COMMANDS.find(({ name }) => name === command.name);

  if (!response) {
    const message = '\'response\' should be defined';
    throw new EngineError(message, { reason: 'SERVER_INPUT_VALIDATION' });
  }

  const { data, metadata } = response;

  if (!data) {
    const message = '\'response.data\' should be defined';
    throw new EngineError(message, { reason: 'SERVER_INPUT_VALIDATION' });
  }

  if (!metadata) {
    const message = '\'response.metadata\' should be defined';
    throw new EngineError(message, { reason: 'SERVER_INPUT_VALIDATION' });
  }

  if (multiple && !Array.isArray(data)) {
    const message = `'response.data' should be an array, not '${data}'`;
    throw new EngineError(message, { reason: 'SERVER_INPUT_VALIDATION' });
  }

  if (!multiple && data.constructor !== Object) {
    const message = `'response.data' should be an object, not '${data}'`;
    throw new EngineError(message, { reason: 'SERVER_INPUT_VALIDATION' });
  }

  if (multiple && !Array.isArray(metadata)) {
    const message = `'response.metadata' should be an array, not '${metadata}'`;
    throw new EngineError(message, { reason: 'SERVER_INPUT_VALIDATION' });
  }

  if (!multiple && metadata.constructor !== Object) {
    const message = `'response.metadata' should be an object, not '${metadata}'`;
    throw new EngineError(message, { reason: 'SERVER_INPUT_VALIDATION' });
  }
};

module.exports = {
  validateResponse,
};
<commit_msg>Small refactoring<commit_after>'use strict';

const { COMMANDS } = require('../../../constants');
const { EngineError } = require('../../../error');

// Check output, for the errors that should not happen,
// i.e. server-side (e.g. 500)
// In short: response should be an array of objects
const validateResponse = function ({ command, response }) {
  const { multiple } = COMMANDS.find(({ name }) => name === command.name);

  if (!response) {
    const message = '\'response\' should be defined';
    throw new EngineError(message, { reason: 'SERVER_INPUT_VALIDATION' });
  }

  const { data, metadata } = response;
  validateData({ data, multiple });
  validateMetadata({ metadata, multiple });
};

const validateData = function ({ data, multiple }) {
  if (!data) {
    const message = '\'response.data\' should be defined';
    throw new EngineError(message, { reason: 'SERVER_INPUT_VALIDATION' });
  }

  if (multiple && !Array.isArray(data)) {
    const message = `'response.data' should be an array, not '${data}'`;
    throw new EngineError(message, { reason: 'SERVER_INPUT_VALIDATION' });
  }

  if (!multiple && data.constructor !== Object) {
    const message = `'response.data' should be an object, not '${data}'`;
    throw new EngineError(message, { reason: 'SERVER_INPUT_VALIDATION' });
  }
};

const validateMetadata = function ({ metadata, multiple }) {
  if (!metadata) {
    const message = '\'response.metadata\' should be defined';
    throw new EngineError(message, { reason: 'SERVER_INPUT_VALIDATION' });
  }

  if (multiple && !Array.isArray(metadata)) {
    const message = `'response.metadata' should be an array, not '${metadata}'`;
    throw new EngineError(message, { reason: 'SERVER_INPUT_VALIDATION' });
  }

  if (!multiple && metadata.constructor !== Object) {
    const message = `'response.metadata' should be an object, not '${metadata}'`;
    throw new EngineError(message, { reason: 'SERVER_INPUT_VALIDATION' });
  }
};

module.exports = {
  validateResponse,
};
<|endoftext|>


<commit_before>define('app/models/machine', ['ember'],
    /**
     * Machine model
     *
     * Also check state mapping in config.py
     * @returns Class
     */
    function() {
        return Ember.Object.extend({
            id: null,

            imageId: null,

            image: null,
            name: null,
            backend: null,
            selected: false,
            hasKey: false,
            hasMonitoring: false,
            state: 'stopped',

            reboot: function(){
                console.log('Rebooting machine', this.name);

                var that = this;
                $.ajax({
                    url: '/backends/' + this.backend.index + '/machines/' + this.id,
                    type: 'POST',
                    data: 'action=reboot',
                    success: function(data) {
                        that.set('state', 'rebooting');
                        console.info('Succesfully sent reboot to machine', that.name);
                    },
                    error: function(jqXHR, textstate, errorThrown) {
                        Mist.notificationController.notify('Error when sending reboot to machine ' +
                                that.name);
                        console.error(textstate, errorThrown, 'when sending reboot to machine',
                                that.name);
                    }
                });
            },

            destroy: function(){
                console.log('Destroying machine', this.name);

                var that = this
                $.ajax({
                    url: '/backends/' + this.backend.index + '/machines/' + this.id,
                    type: 'POST',
                    data: 'action=destroy',
                    success: function(data) {
                        that.set('state', 'pending');
                        console.info('Successfully sent destroy to machine', that.name);
                    },
                    error: function(jqXHR, textstate, errorThrown) {
                        Mist.notificationController.notify('Error when sending destroy to machine ' +
                                that.name);
                        console.error(textstate, errorThrown, 'when sending destroy to machine',
                                that.name);
                    }
                });
            },

            start: function(){
                console.log('Starting machine', this.name);

                var that = this;
                $.ajax({
                    url: 'backends/' + this.backend.index + '/machines/' + this.id,
                    type: 'POST',
                    data: 'action=start',
                    success: function(data) {
                        that.set('state', 'pending');
                        console.info('Successfully sent start to machine', that.name);
                    },
                    error: function(jqXHR, textstate, errorThrown) {
                        Mist.notificationController.notify('Error when sending start to machine ' +
                                that.name);
                        console.error(textstate, errorThrown, 'when sending start to machine',
                                that.name);
                    }
                });
            },

            shutdown: function(){
                console.log('Stopping machine', this.name);

                var that = this;
                $.ajax({
                    url: 'backends/' + this.backend.index + '/machines/' + this.id,
                    type: 'POST',
                    data: 'action=stop',
                    success: function(data) {
                        that.set('state', 'stopped');
                        console.info('Successfully sent stop to machine', that.name);
                    },
                    error: function(jqXHR, textstate, errorThrown) {
                        Mist.notificationController.notify('Error when sending stop to machine ' +
                                that.name);
                        console.error(textstate, errorThrown, 'when sending stop to machine',
                                that.name);
                    }
                });
            },

            shell: function(shell_command, callback){
                console.log('Sending', shell_command, 'to machine', this.name);

                var host;
                if (this.extra.dns_name) {
                    // it is an ec2 machine so it has dns_name
                    host = this.extra.dns_name;
                } else {
                    // if not in ec2, it should have a public ip
                    host = this.public_ips[0];
                }

                // In case of ec2, mist.io could have set this. Server can handle empty string.
                var ssh_user;
                try {
                    ssh_user = this.extra.tags.ssh_user;
                } catch (error) {
                    ssh_user = 'root';
                }

                var that = this;
                $.ajax({
                    url: '/backends/' + this.backend.index + '/machines/' + this.id + '/shell',
                    type: 'POST',
                    data: {'host': host,
                           'ssh_user': ssh_user,
                           'command': shell_command},
                    success: function(data) {
                        if (data){
                            callback(data);
                        }
                        console.info('Successfully sent shell command', shell_command, 'to machine',
                                that.name, 'with result:\n', data);
                    },
                    error: function(jqXHR, textstate, errorThrown) {
                        Mist.notificationController.notify('Error sending shell command ' +
                                shell_command + ' to machine ' + that.name);
                        console.error(textstate, errorThrown, 'when sending shell command',
                                shell_command, 'to machine', that.name);
                    }
                });

            },

            hasAlert : function(){
                //TODO when we have alerts
                return false;
            }.property('hasAlert'),

            startUptimeTimer: function() {
                var that = this;

                setInterval(function() {
                    if (that.get('state' != 'stopped') || !that.get('uptimeFromServer') ||
                        !that.get('uptimeChecked')) {

                        return;

                    } else {
                        that.set('uptime', that.get('uptimeFromServer') + (Date.now()
                                           - that.get('uptimeChecked')));
                    }
                }, 1000);
            },

            checkUptime: function(){
                if (this.hasKey) {
                    var host;
                    if (this.extra.dns_name) {
                        // it is ec2 machine
                        host = this.extra.dns_name;
                    } else {
                        // if not ec2 it should have a public ip
                        host = this.public_ips[0];
                    }

                    // In case of ec2, mist.io could have set this. Server can handle empty string.
                    var ssh_user;
                    try {
                        ssh_user = this.extra.tags.ssh_user;
                    } catch (error) {
                        ssh_user = 'root';
                    }

                    var that = this;
                    $.ajax({
                        url: '/backends/' + this.backend.index + '/machines/' + this.id + '/shell',
                        type: 'POST',
                        data: {'host': host,
                               'ssh_user': ssh_user,
                               'command': 'cat /proc/uptime'},
                        success: function(data) {
                            var resp = data.split(' ');
                            if (resp.length == 2) {
                                var uptime = parseFloat(resp[0]) * 1000;
                                that.set('uptimeChecked', Date.now());
                                that.set('uptimeFromServer', uptime);
                            }
                            console.info('Successfully got uptime', data, 'from machine', that.name);
                        },
                        error: function(jqXHR, textstate, errorThrown) {
                            Mist.notificationController.notify('Error getting uptime from machine ' +
                                    that.name);
                            console.error(textstate, errorThrown, 'when getting uptime from machine',
                                    that.name);
                        }
                    });
                }
            },

            checkHasMonitoring: function(){
                var that = this;
                $.ajax({
                    url: 'backends/' + this.backend.index + '/machines/' + this.id + '/monitoring',
                    success: function(data) {
                        console.log("machine has monitoring");
                        console.log(data);
                        if('monitoring' in data){
                            that.set('hasMonitoring', data.monitoring);
                        }
                    },
                    error: function(jqXHR, textstate, errorThrown) {
                            Mist.notificationController.notify('Error checking monitoring of machine ' +
                                    that.name);
                            console.error(textstate, errorThrown, 'while checking monitoring of machine',
                                    that.name);
                    }
                });
            },

            checkHasKey: function(){
                var host;
                if (this.extra.dns_name) {
                    // it is ec2 machine
                    host = this.extra.dns_name;
                } else {
                    // if not ec2 it should have a public ip
                    host = this.public_ips[0];
                }

                // In case of ec2, mist.io could have set this. Server can handle empty string.
                var ssh_user;
                try {
                    ssh_user = this.extra.tags.ssh_user;
                } catch (error) {
                    ssh_user = 'root';
                }

                var that = this;
                $.ajax({
                    url: '/backends/' + this.backend.index + '/machines/' + this.id + '/shell',
                    type: 'POST',
                    data: {'host': host,
                           'ssh_user': ssh_user,
                           'command': 'cat /proc/uptime'},
                    success: function(data) {
                        that.set('hasKey', true);
                        // TODO: since we got it let's use it, checkUptime does the same.
                        var resp = data.split(' ');
                        if (resp.length == 2) {
                            var uptime = parseFloat(resp[0]) * 1000;
                            that.set('uptimeChecked', Date.now());
                            that.set('uptimeFromServer', uptime);
                        }
                        console.info('We have key for machine', that.name);
                    },
                    error: function(jqXHR, textstate, errorThrown) {
                        that.set('hasKey', false);
                        Mist.notificationController.notify('Error while checking key of machine ' +
                                that.name);
                        console.error(textstate, errorThrown, 'while checking key of machine', that.name);
                    }
                });
            },

            resetUptime: function(){
                if (this.state != 'stopped') {
                    this.set('uptime', 0);
                    this.uptimeTimer = false;
                } else {
                    if (this.get('uptime') == 0) {
                        // TODO: This is used only here, can we skip checkUptime?
                        this.checkUptime();
                    }
                }
            }.observes('state'),

            monitoringChanged: function(){
                var oldValue = !this.hasMonitoring;
                console.log("monitoring:  " + oldValue);

                var that = this;

                var host;
                if (this.extra.dns_name) {
                    // it is ec2 machine
                    host = this.extra.dns_name;
                } else {
                    // if not ec2 it should have a public ip
                    host = this.public_ips[0];
                }

                var payload = {
                   'monitoring': this.hasMonitoring,
                   'host': host,
                   'provider': this.backend.provider
                };

                $.ajax({
                    // TODO: this should point to https://mist.io/....
                    url: 'backends/' + this.backend.index + '/machines/' + this.id + '/monitoring',
                    type: 'POST',
                    contentType: 'application/json',
                    dataType: 'json',
                    data: JSON.stringify(payload),
                    success: function(data) {

                    },
                    error: function(jqXHR, textstate, errorThrown) {
                        that.set('hasMonitoring', oldValue);
                    }
                });
            }.observes('hasMonitoring'),

            init: function() {
                this._super();
                var that = this;
                this.backend.images.getImage(this.imageId, function(image) {
                    that.set('image', image);
                });
                this.startUptimeTimer();
                this.checkHasKey();
                this.checkHasMonitoring();
            }

        });
    }
);
<commit_msg>Change ordering of arguments<commit_after>define('app/models/machine', ['ember'],
    /**
     * Machine model
     *
     * Also check state mapping in config.py
     * @returns Class
     */
    function() {
        return Ember.Object.extend({
            id: null,

            imageId: null,

            image: null,
            name: null,
            backend: null,
            selected: false,
            hasKey: false,
            hasMonitoring: false,
            state: 'stopped',

            reboot: function(){
                console.log('Rebooting machine', this.name);

                var that = this;
                $.ajax({
                    url: '/backends/' + this.backend.index + '/machines/' + this.id,
                    type: 'POST',
                    data: 'action=reboot',
                    success: function(data) {
                        that.set('state', 'rebooting');
                        console.info('Succesfully sent reboot to machine', that.name);
                    },
                    error: function(jqXHR, textstate, errorThrown) {
                        Mist.notificationController.notify('Error when sending reboot to machine ' +
                                that.name);
                        console.error(textstate, errorThrown, 'when sending reboot to machine',
                                that.name);
                    }
                });
            },

            destroy: function(){
                console.log('Destroying machine', this.name);

                var that = this
                $.ajax({
                    url: '/backends/' + this.backend.index + '/machines/' + this.id,
                    type: 'POST',
                    data: 'action=destroy',
                    success: function(data) {
                        that.set('state', 'pending');
                        console.info('Successfully sent destroy to machine', that.name);
                    },
                    error: function(jqXHR, textstate, errorThrown) {
                        Mist.notificationController.notify('Error when sending destroy to machine ' +
                                that.name);
                        console.error(textstate, errorThrown, 'when sending destroy to machine',
                                that.name);
                    }
                });
            },

            start: function(){
                console.log('Starting machine', this.name);

                var that = this;
                $.ajax({
                    url: 'backends/' + this.backend.index + '/machines/' + this.id,
                    type: 'POST',
                    data: 'action=start',
                    success: function(data) {
                        that.set('state', 'pending');
                        console.info('Successfully sent start to machine', that.name);
                    },
                    error: function(jqXHR, textstate, errorThrown) {
                        Mist.notificationController.notify('Error when sending start to machine ' +
                                that.name);
                        console.error(textstate, errorThrown, 'when sending start to machine',
                                that.name);
                    }
                });
            },

            shutdown: function(){
                console.log('Stopping machine', this.name);

                var that = this;
                $.ajax({
                    url: 'backends/' + this.backend.index + '/machines/' + this.id,
                    type: 'POST',
                    data: 'action=stop',
                    success: function(data) {
                        that.set('state', 'stopped');
                        console.info('Successfully sent stop to machine', that.name);
                    },
                    error: function(jqXHR, textstate, errorThrown) {
                        Mist.notificationController.notify('Error when sending stop to machine ' +
                                that.name);
                        console.error(textstate, errorThrown, 'when sending stop to machine',
                                that.name);
                    }
                });
            },

            shell: function(shell_command, callback){
                console.log('Sending', shell_command, 'to machine', this.name);

                var host;
                if (this.extra.dns_name) {
                    // it is an ec2 machine so it has dns_name
                    host = this.extra.dns_name;
                } else {
                    // if not in ec2, it should have a public ip
                    host = this.public_ips[0];
                }

                // In case of ec2, mist.io could have set this. Server can handle empty string.
                var ssh_user;
                try {
                    ssh_user = this.extra.tags.ssh_user;
                } catch (error) {
                    ssh_user = 'root';
                }

                var that = this;
                $.ajax({
                    url: '/backends/' + this.backend.index + '/machines/' + this.id + '/shell',
                    type: 'POST',
                    data: {'host': host,
                           'ssh_user': ssh_user,
                           'command': shell_command},
                    success: function(data) {
                        if (data){
                            callback(data);
                        }
                        console.info('Successfully sent shell command', shell_command, 'to machine',
                                that.name, 'with result:\n', data);
                    },
                    error: function(jqXHR, textstate, errorThrown) {
                        Mist.notificationController.notify('Error sending shell command ' +
                                shell_command + ' to machine ' + that.name);
                        console.error(textstate, errorThrown, 'when sending shell command',
                                shell_command, 'to machine', that.name);
                    }
                });

            },

            hasAlert : function(){
                //TODO when we have alerts
                return false;
            }.property('hasAlert'),

            startUptimeTimer: function() {
                var that = this;

                setInterval(function() {
                    if (that.get('state' != 'stopped') || !that.get('uptimeFromServer') ||
                        !that.get('uptimeChecked')) {

                        return;

                    } else {
                        that.set('uptime', that.get('uptimeFromServer') + (Date.now()
                                           - that.get('uptimeChecked')));
                    }
                }, 1000);
            },

            checkUptime: function(){
                if (this.hasKey) {
                    var host;
                    if (this.extra.dns_name) {
                        // it is ec2 machine
                        host = this.extra.dns_name;
                    } else {
                        // if not ec2 it should have a public ip
                        host = this.public_ips[0];
                    }

                    // In case of ec2, mist.io could have set this. Server can handle empty string.
                    var ssh_user;
                    try {
                        ssh_user = this.extra.tags.ssh_user;
                    } catch (error) {
                        ssh_user = 'root';
                    }

                    var that = this;
                    $.ajax({
                        url: '/backends/' + this.backend.index + '/machines/' + this.id + '/shell',
                        type: 'POST',
                        data: {'host': host,
                               'ssh_user': ssh_user,
                               'command': 'cat /proc/uptime'},
                        success: function(data) {
                            var resp = data.split(' ');
                            if (resp.length == 2) {
                                var uptime = parseFloat(resp[0]) * 1000;
                                that.set('uptimeChecked', Date.now());
                                that.set('uptimeFromServer', uptime);
                            }
                            console.info('Successfully got uptime', data, 'from machine', that.name);
                        },
                        error: function(jqXHR, textstate, errorThrown) {
                            Mist.notificationController.notify('Error getting uptime from machine ' +
                                    that.name);
                            console.error(textstate, errorThrown, 'when getting uptime from machine',
                                    that.name);
                        }
                    });
                }
            },

            checkHasMonitoring: function(){
                var that = this;
                $.ajax({
                    url: 'backends/' + this.backend.index + '/machines/' + this.id + '/monitoring',
                    success: function(data) {
                        console.log("machine has monitoring");
                        console.log(data);
                        if('monitoring' in data){
                            that.set('hasMonitoring', data.monitoring);
                        }
                    },
                    error: function(jqXHR, textstate, errorThrown) {
                            Mist.notificationController.notify('Error checking monitoring of machine ' +
                                    that.name);
                            console.error(textstate, errorThrown, 'while checking monitoring of machine',
                                    that.name);
                    }
                });
            },

            checkHasKey: function(){
                var host;
                if (this.extra.dns_name) {
                    // it is ec2 machine
                    host = this.extra.dns_name;
                } else {
                    // if not ec2 it should have a public ip
                    host = this.public_ips[0];
                }

                // In case of ec2, mist.io could have set this. Server can handle empty string.
                var ssh_user;
                try {
                    ssh_user = this.extra.tags.ssh_user;
                } catch (error) {
                    ssh_user = 'root';
                }

                var that = this;
                $.ajax({
                    url: '/backends/' + this.backend.index + '/machines/' + this.id + '/shell',
                    type: 'POST',
                    data: {'host': host,
                           'ssh_user': ssh_user,
                           'command': 'cat /proc/uptime'},
                    success: function(data) {
                        that.set('hasKey', true);
                        // TODO: since we got it let's use it, checkUptime does the same.
                        var resp = data.split(' ');
                        if (resp.length == 2) {
                            var uptime = parseFloat(resp[0]) * 1000;
                            that.set('uptimeChecked', Date.now());
                            that.set('uptimeFromServer', uptime);
                        }
                        console.info('We have key for machine', that.name);
                    },
                    error: function(jqXHR, textstate, errorThrown) {
                        that.set('hasKey', false);
                        Mist.notificationController.notify('Error while checking key of machine ' +
                                that.name);
                        console.error(textstate, errorThrown, 'while checking key of machine', that.name);
                    }
                });
            },

            resetUptime: function(){
                if (this.state != 'stopped') {
                    this.set('uptime', 0);
                    this.uptimeTimer = false;
                } else {
                    if (this.get('uptime') == 0) {
                        // TODO: This is used only here, can we skip checkUptime?
                        this.checkUptime();
                    }
                }
            }.observes('state'),

            monitoringChanged: function(){
                var oldValue = !this.hasMonitoring;
                console.log("monitoring:  " + oldValue);

                var that = this;

                var host;
                if (this.extra.dns_name) {
                    // it is ec2 machine
                    host = this.extra.dns_name;
                } else {
                    // if not ec2 it should have a public ip
                    host = this.public_ips[0];
                }

                var payload = {
                   'monitoring': this.hasMonitoring,
                   'host': host,
                   'provider': this.backend.provider
                };

                $.ajax({
                    // TODO: this should point to https://mist.io/....
                    url: 'backends/' + this.backend.index + '/machines/' + this.id + '/monitoring',
                    type: 'POST',
                    contentType: 'application/json',
                    data: JSON.stringify(payload),
                    dataType: 'json',
                    success: function(data) {

                    },
                    error: function(jqXHR, textstate, errorThrown) {
                        that.set('hasMonitoring', oldValue);
                    }
                });
            }.observes('hasMonitoring'),

            init: function() {
                this._super();
                var that = this;
                this.backend.images.getImage(this.imageId, function(image) {
                    that.set('image', image);
                });
                this.startUptimeTimer();
                this.checkHasKey();
                this.checkHasMonitoring();
            }

        });
    }
);
<|endoftext|>


<commit_before>/*
 * eval.js - Evaluate JavaScript code in Discord.
 *
 * Contributed by Ovyerus.
 */

/* eslint-disable no-unused-vars */
const Eris = require('eris');
const util = require('util');
const utils = require(`${__baseDir}/modules/utils.js`);
const fs = require('fs');
const cp = require('child_process');
const path = require('path');
const got = require('got');
/* eslint-enable */

const FAILCOL = 0xF44336;
const SUCCESSCOL = 0x8BC34A;

exports.commands = [
    'eval'
];

exports.eval = {
    desc: 'Evaluate code in Discord.',
    fullDesc: 'Used to evaluate JavaScript code in Discord. Mostly for debug purposes.',
    adminOnly: true,
    usage: '<code>',
    main(bot, ctx) {
        return new Promise((resolve, reject) => {
            if (ctx.suffix.length === 0) {
                ctx.msg.channel.createMessage('Please give arguments to evaluate.').then(resolve).catch(reject);
            } else {
                let {msg, args, cmd, suffix, cleanSuffix, guildBot, settings} = ctx; // eslint-disable-line
                try {
                    let returned = eval(ctx.suffix);
                    let str = util.inspect(returned, {depth: 1});
                    str = str.replace(new RegExp(bot.token, 'gi'), '(token)');

                    let embed = {
                        title: 'Evaluation Results',
                        color: SUCCESSCOL,
                        fields: [
                            {name: 'Input', value: generateCodeblock(ctx.suffix)},
                            {name: 'Output', value: generateCodeblock(str)}
                        ]
                    };

                    if (returned instanceof Promise) embed.fields[1].value = generateCodeblock('[object Promise]');

                    if (str.length < 1000) {
                        sendEval(bot, ctx, embed, returned).then(resolve).catch(reject);
                    } else {
                        got('https://hastebin.com/documents', {
                            method: 'POST',
                            body: str
                        }).then(res => {
                            let key = JSON.parse(res.body).key;
                            let url = `https://hastebin.com/${key}.js`;
                            embed.fields[1].value = `Output is too long to display nicely.\nOutput has been uploaded [here](${url})`;

                            return sendEval(bot, ctx, embed, returned);
                        }).then(resolve).catch(reject);
                    }
                } catch(err) {
                    let embed = {
                        title: 'Evaluation Results',
                        color: FAILCOL,
                        fields: [
                            {name: 'Input', value: generateCodeblock(ctx.suffix)},
                            {name: 'Error', value: generateCodeblock(err)}
                        ]
                    };

                    ctx.msg.channel.createMessage({embed}).then(resolve).catch(reject);
                }
            }
        });
    }
};

function generateCodeblock(text) {
    return `\`\`\`js\n${text}\n\`\`\``;
}

function sendEval(bot, ctx, embed, returned) {
    return new Promise((resolve, reject) => {
        let outer;
        ctx.msg.channel.createMessage({embed}).then(m => {
            outer = m;
            if (returned instanceof Promise) {
                return returned;
            } else {
                return null;
            }
        }).then(res => {
            if (!res) {
                resolve();
                return null;
            } else {
                let strN = util.inspect(returned._rejectionHandler0, {depth: 1});
                strN = strN.replace(new RegExp(bot.token, 'gi'), '(token)');

                if (strN.length >= 1000) {
                    return got('https://hastebin.com/documents', {
                        method: 'POST',
                        body: strN
                    });
                } else {
                    return outer.edit({embed: {
                        title: 'Evaluation Results',
                        color: SUCCESSCOL,
                        fields: [
                            {name: 'Input', value: generateCodeblock(ctx.suffix)},
                            {name: 'Output', value: generateCodeblock(strN)}
                        ]
                    }});
                }
            }
        }).then(res => {
            if (!res || res instanceof Eris.Message) return null;

            let key = JSON.parse(res.body).key;
            let url = `https://hastebin.com/${key}.js`;

            return outer.edit({embed: {
                title: 'Evaluation Results',
                color: SUCCESSCOL,
                fields: [
                    {name: 'Input', value: generateCodeblock(ctx.suffix)},
                    {name: 'Output', value: `Output is too long to display nicely.\nOutput has been uploaded [here](${url})`}
                ]
            }});
        }).then(resolve).catch(err => {
            if (err.req && err.req._headers.host === 'discordapp.com') {
                reject(err);
                return null;
            } else {
                return outer.edit({embed: {
                    title: 'Evaluation Results',
                    color: FAILCOL,
                    fields: [
                        {name: 'Input', value: generateCodeblock(ctx.suffix)},
                        {name: 'Error', value: generateCodeblock(err)}
                    ]
                }});
            }
        });
    });
}<commit_msg>Add support for displaying just body when sending Got requests<commit_after>/*
 * eval.js - Evaluate JavaScript code in Discord.
 *
 * Contributed by Ovyerus.
 */

/* eslint-disable no-unused-vars */
const Eris = require('eris');
const util = require('util');
const utils = require(`${__baseDir}/modules/utils.js`);
const fs = require('fs');
const cp = require('child_process');
const path = require('path');
const got = require('got');
const PassThrough = require('stream').PassThrough;
/* eslint-enable */

const FAILCOL = 0xF44336;
const SUCCESSCOL = 0x8BC34A;

exports.commands = [
    'eval'
];

exports.eval = {
    desc: 'Evaluate code in Discord.',
    fullDesc: 'Used to evaluate JavaScript code in Discord. Mostly for debug purposes.',
    adminOnly: true,
    usage: '<code>',
    main(bot, ctx) {
        return new Promise((resolve, reject) => {
            if (ctx.suffix.length === 0) {
                ctx.msg.channel.createMessage('Please give arguments to evaluate.').then(resolve).catch(reject);
            } else {
                let {msg, args, cmd, suffix, cleanSuffix, guildBot, settings} = ctx; // eslint-disable-line
                try {
                    let returned = eval(ctx.suffix);
                    let str = util.inspect(returned, {depth: 1});
                    str = str.replace(new RegExp(bot.token, 'gi'), '(token)');

                    let embed = {
                        title: 'Evaluation Results',
                        color: SUCCESSCOL,
                        fields: [
                            {name: 'Input', value: generateCodeblock(ctx.suffix)},
                            {name: 'Output', value: generateCodeblock(str)}
                        ]
                    };

                    if (returned instanceof Promise) embed.fields[1].value = generateCodeblock('[object Promise]');

                    if (str.length < 1000) {
                        sendEval(bot, ctx, embed, returned).then(resolve).catch(reject);
                    } else {
                        got('https://hastebin.com/documents', {
                            method: 'POST',
                            body: str
                        }).then(res => {
                            let key = JSON.parse(res.body).key;
                            let url = `https://hastebin.com/${key}.js`;
                            embed.fields[1].value = `Output is too long to display nicely.\nOutput has been uploaded [here](${url})`;

                            return sendEval(bot, ctx, embed, returned);
                        }).then(resolve).catch(reject);
                    }
                } catch(err) {
                    let embed = {
                        title: 'Evaluation Results',
                        color: FAILCOL,
                        fields: [
                            {name: 'Input', value: generateCodeblock(ctx.suffix)},
                            {name: 'Error', value: generateCodeblock(err)}
                        ]
                    };

                    ctx.msg.channel.createMessage({embed}).then(resolve).catch(reject);
                }
            }
        });
    }
};

function generateCodeblock(text) {
    return `\`\`\`js\n${text}\n\`\`\``;
}

function sendEval(bot, ctx, embed, returned) {
    return new Promise((resolve, reject) => {
        let outer;
        ctx.msg.channel.createMessage({embed}).then(m => {
            outer = m;
            if (returned instanceof Promise) {
                return returned;
            } else {
                return null;
            }
        }).then(res => {
            if (!res) {
                resolve();
                return null;
            } else {
                let strN;
                if (res instanceof PassThrough && res.requestUrl) {
                    strN = res.headers['content-type'].split(';')[0] === 'application/json' ? util.inspect(JSON.parse(res.body), {depth: 1}) : res.body;
                } else {
                    strN = util.inspect(res, {depth: 1});
                }
                strN = strN.replace(new RegExp(bot.token, 'gi'), '(token)');

                if (strN.length >= 1000) {
                    return got('https://hastebin.com/documents', {
                        method: 'POST',
                        body: strN
                    });
                } else {
                    return outer.edit({embed: {
                        title: 'Evaluation Results',
                        color: SUCCESSCOL,
                        fields: [
                            {name: 'Input', value: generateCodeblock(ctx.suffix)},
                            {name: 'Output', value: generateCodeblock(strN)}
                        ]
                    }});
                }
            }
        }).then(res => {
            if (!res || res instanceof Eris.Message) return null;

            let key = JSON.parse(res.body).key;
            let url = `https://hastebin.com/${key}.js`;

            return outer.edit({embed: {
                title: 'Evaluation Results',
                color: SUCCESSCOL,
                fields: [
                    {name: 'Input', value: generateCodeblock(ctx.suffix)},
                    {name: 'Output', value: `Output is too long to display nicely.\nOutput has been uploaded [here](${url})`}
                ]
            }});
        }).then(resolve).catch(err => {
            if (err.req && err.req._headers.host === 'discordapp.com') {
                reject(err);
                return null;
            } else {
                return outer.edit({embed: {
                    title: 'Evaluation Results',
                    color: FAILCOL,
                    fields: [
                        {name: 'Input', value: generateCodeblock(ctx.suffix)},
                        {name: 'Error', value: generateCodeblock(err)}
                    ]
                }});
            }
        });
    });
}<|endoftext|>


<commit_before>
$(document).ready(function() {

  // https://github.com/js-cookie/js-cookie
  // each post id looks like: workout-series#-week#-day#

  /*
      store user's workout log
      only stores checked workouts, as 'true'
      if it's not checked it'll act as undefined

      looks like: where workout-series-week-day

      workoutLog [
        'workout-1-2-3',
        'workout-1-2-4',
      ]
  */

  // let workoutLog = getWorkouts();
  // console.log(workoutLog);
  // processWorkouts(workoutLog)
  // setWeekCleared();

  // populate log from cookies
  // console.log('hey');
  // Cookies.set('name', { foo: 'bar' });
  // let storage = Cookies.getJSON('name');
  // console.log(storage.foo)

  $('.workout .trigger').on( "click", function() {
    const workout = $(this).parent();
    if( $(workout).hasClass('checked') ) {
      $(workout).removeClass('checked');
      setWeekCleared();
      repositionWeek($(workout).parent());
      removeFromLog($(workout).attr('id'), workoutLog);
      setWorkouts(workoutLog)
    } else {
      $(workout).addClass('checked');
      repositionWeek($(workout).parent());
      addToLog($(workout), workoutLog);
      setWorkouts(workoutLog);
      setWeekCleared();
    }
  })

});

//
//  Process the DOM
//

function repositionWeek(week) {
  $('.week .workout').each(function() {

    if($(this).hasClass('checked')) {
      $(this).appendTo($(this).parent());
    }
  });
}

// runs through a week's workouts; folds it up if it's all donw
function setWeekCleared() {
  $('.week').each(function() {
    let cleared = true;
    $(this).find('.workout:not(.checked)').each(function() {
      cleared = false;
    });
    if(!cleared) {
      $(this).removeClass('cleared');
    } else {
      $(this).addClass('cleared');
    }
  });
}

// given a log, search all the matching IDs and set them to checked
function processWorkouts(log) {
  log.forEach(function (workout) {
    $('#'+workout).addClass('checked');
  })

  $('.week').each(function() {
    repositionWeek($(this))
  })
}

//
//  workout log
//

function addToLog(workoutObj, log) {
 const series = workoutObj.data('series'), week = workoutObj.data('week'), day = workoutObj.data('day');
 const workout = `workout-${series}-${week}-${day}`

 // don't add dupes
 if ($.inArray( workout, log ) < 0 )
  log.push(workout);
}

// takes an id that matches the value
function removeFromLog(idName, log) {
  const position = $.inArray( idName, log );
  if( position > -1 ) {
    log.splice(position, 1);
  };
}

//
//  cookie / storage handling
//

function setWorkouts(workoutLog) {
  Cookies.set('workoutLog', workoutLog);
  console.log(Cookies.getJSON('workoutLog'))
}
function getWorkouts() {
  return Cookies.getJSON('workoutLog') || [];
}

<commit_msg>bug reset<commit_after>
// $(document).ready(function() {

//   // https://github.com/js-cookie/js-cookie
//   // each post id looks like: workout-series#-week#-day#

//   /*
//       store user's workout log
//       only stores checked workouts, as 'true'
//       if it's not checked it'll act as undefined

//       looks like: where workout-series-week-day

//       workoutLog [
//         'workout-1-2-3',
//         'workout-1-2-4',
//       ]
//   */

//   // let workoutLog = getWorkouts();
//   // console.log(workoutLog);
//   // processWorkouts(workoutLog)
//   // setWeekCleared();

//   // populate log from cookies
//   // console.log('hey');
//   // Cookies.set('name', { foo: 'bar' });
//   // let storage = Cookies.getJSON('name');
//   // console.log(storage.foo)

//   $('.workout .trigger').on( "click", function() {
//     const workout = $(this).parent();
//     if( $(workout).hasClass('checked') ) {
//       $(workout).removeClass('checked');
//       setWeekCleared();
//       repositionWeek($(workout).parent());
//       removeFromLog($(workout).attr('id'), workoutLog);
//       setWorkouts(workoutLog)
//     } else {
//       $(workout).addClass('checked');
//       repositionWeek($(workout).parent());
//       addToLog($(workout), workoutLog);
//       setWorkouts(workoutLog);
//       setWeekCleared();
//     }
//   })

// });

// //
// //  Process the DOM
// //

// function repositionWeek(week) {
//   $('.week .workout').each(function() {

//     if($(this).hasClass('checked')) {
//       $(this).appendTo($(this).parent());
//     }
//   });
// }

// // runs through a week's workouts; folds it up if it's all donw
// function setWeekCleared() {
//   $('.week').each(function() {
//     let cleared = true;
//     $(this).find('.workout:not(.checked)').each(function() {
//       cleared = false;
//     });
//     if(!cleared) {
//       $(this).removeClass('cleared');
//     } else {
//       $(this).addClass('cleared');
//     }
//   });
// }

// // given a log, search all the matching IDs and set them to checked
// function processWorkouts(log) {
//   log.forEach(function (workout) {
//     $('#'+workout).addClass('checked');
//   })

//   $('.week').each(function() {
//     repositionWeek($(this))
//   })
// }

// //
// //  workout log
// //

// function addToLog(workoutObj, log) {
//  const series = workoutObj.data('series'), week = workoutObj.data('week'), day = workoutObj.data('day');
//  const workout = `workout-${series}-${week}-${day}`

//  // don't add dupes
//  if ($.inArray( workout, log ) < 0 )
//   log.push(workout);
// }

// // takes an id that matches the value
// function removeFromLog(idName, log) {
//   const position = $.inArray( idName, log );
//   if( position > -1 ) {
//     log.splice(position, 1);
//   };
// }

// //
// //  cookie / storage handling
// //

// function setWorkouts(workoutLog) {
//   Cookies.set('workoutLog', workoutLog);
//   console.log(Cookies.getJSON('workoutLog'))
// }
// function getWorkouts() {
//   return Cookies.getJSON('workoutLog') || [];
// }

<|endoftext|>


<commit_before>(function () {

  var Pontoon = {
        _doc: window,
        _ptn: window.top,
        _locale: "",
        _meta: {},
        _data: {}
  	  },
      jqueryAppended = false,
      script = document.createElement('script');

  // Main code
  function jqueryLoaded() {
    $.noConflict();
    jQuery(document).ready(function($) {

      /**
       * Send data to main Pontoon code
       */
      function sendData() {
        // Deep copy: http://api.jquery.com/jQuery.extend
        var data = $.extend(true, {}, Pontoon._data);

        $(data.entities).each(function () {
          delete this.node;
        });
        Pontoon._ptn.postMessage(JSON.stringify(data), "*");
      }

      /**
       * Extend entity object
       * TODO: move to main.js
       *
       * e Temporary entity object
       */
      function extendEntity(e) {
        e.hover = function () {
          this.node.get(0).showToolbar();
          this.ui.toggleClass('hovered');
        };
        e.unhover = function () {
          this.node.get(0).hideToolbar();
          this.ui.toggleClass('hovered');
        };
      }

      /**
       * Extract entities from the document, not prepared for working with Pontoon
       *
       * Create entity object from every non-empty text node
       * Exclude nodes from special tags, e.g.


<commit_before>// Time-stamp: "2015-05-21 12:47:56 phil"
// Section 7x is for Conkeror internal tweaks (?)
dumpln("72theme");

// Example: darken Astronomy Picture of the Day (APOD)
// register_user_stylesheet(
//     make_css_data_uri(["body{background: black !important; color: grey !important;}",
//                        ":link, :link * {color: #4986dd !important;}",
//                        ":visited, :visited * {color: #d75047 !important;}"],
//                       $url_prefixes = "http://apod.nasa.gov/apod/"));

// Based on http://www.featureblend.com/css-json.html

// With inheritance:
// var cssjson = {
//     ".copy-1":{
//         "font-family":"Verdana, Geneva, Arial, Helvetica, sans-serif",
//         "font-size":"11px",
//         "color":"#CCC"
//     },
//     "div#container div#header":{
//         "CSSJSON-INHERIT-SELECTOR":".copy-1",
//         "position":"absolute",
//         "top":"12px",
//         "left":"4px"
//     }
// }

// TODO write tests
function ph_generate_css (json) {
    var styleStr = "";
    for (var i in json) {
        styleStr += i + " {";
        for (var j in json[i]) {
            if (j == "CSSJSON-INHERIT-SELECTOR") {
                for (var k in json[json[i][j]]) {
                    styleStr += k + ": \"" + json[json[i][j]][k] + " !important\"; ";
                }
            } else {
                styleStr += j + ": \"" + json[i][j] + " !important\"; ";
            }
        }
        styleStr += "} ";
    }
    return styleStr;
}

var ph_dark_css = {
    "*" : {
        "font-style"                : "normal"            ,
        "background"                : "#404040"           ,
        "color"                     : "#cccccc"           ,
        "text-shadow"               : "0px 0px 0px black" ,
        "-moz-border-bottom-colors" : "#505050 #505050"   ,
        "-moz-border-top-colors"    : "#505050 #505050"   ,
        "-moz-border-right-colors"  : "#505050 #505050"   ,
        "-moz-border-left-colors"   : "#505050 #505050"   ,
        "box-shadow"                : "0 0 0 black"       ,
        "-moz-box-shadow"           : "0 0 0 black"       ,
        "border-color"              : "#505050"           ,
        "text-decoration"           : "none"
    },
    "code, pre, code *, pre *" : {
        "color"                     : "#f6f3e8"           ,
        "background-color"          : "#353535"
    },
    ":link, :link *" : {
        "color"                     : "#8ac6f2"
    },
    ":visited, :visited *" : {
        "color"                     : "#95e454"
    },
    "h1, h2, h3, h4, h5, h6" : {
        "color"                     : "white"
    },
    "em" : {
        "background-color"          : "#505050"
    }
};

function darken_page (I) {
    const styles = ph_generate_css(ph_dark_css);
    const document = I.window.buffers.current.document;
    if (document.createStyleSheet) {
        document.createStyleSheet("javascript:'" + styles + "'");
    } else {
        const newSS = document.createElement('link');
        newSS.rel = 'stylesheet';
        newSS.href = 'data:text/css,' + escape(styles);
        document.getElementsByTagName("head")[0].appendChild(newSS);
    }
}

// function darken_page (I) {
//     var newSS, styles='* { background: #404040 ! important; color: #cccccc !important }'
//         + ':link, :link * { color: #8ac6f2 !important }'
//         + ':visited, :visited * { color: #95e454 !important }';

//     var document = I.window.buffers.current.document;

//     if (document.createStyleSheet) {
//         document.createStyleSheet("javascript:'" + styles + "'");
//     } else {
//         newSS=document.createElement('link');
//         newSS.rel='stylesheet';
//         newSS.href='data:text/css,'+escape(styles);
//         document.getElementsByTagName("head")[0].appendChild(newSS);
//     }
// }

interactive("darken-page", "Darken the page in an attempt to save your eyes.",
            darken_page);

define_key(content_buffer_normal_keymap, "C-M-d", "darken-page");

/*
let (home = get_home_directory()) {
    home.appendRelativePath(".conkerorrc/stylesheets/facebook-hello-kitty.css");
    register_user_stylesheet(make_uri(home).spec);
}
*/

// apod: darken
register_user_stylesheet(
    make_css_data_uri(["body{background: black !important; color: grey !important;}",
                       ":link, :link * {color: #4986dd !important;}",
                       ":visited, :visited * {color: #d75047 !important;}"],
                      $url_prefixes = "http://apod.nasa.gov/apod/"));

// conkerorwiki
register_user_stylesheet(
    make_css_data_uri(
        ["html, body { background-color: #666 !important; }",
         "#page { padding: 2px 0px !important; }",
         "#content { padding: 0px 20px; }",
         "#header, #page, #footer { max-width: 65em; background-color: white; }"],
        $url_prefixes = "http://conkeror.org"));

// graaa fix background
register_user_stylesheet(
    make_css_data_uri("body {background-color:black !important; color: white;}",
                      $url_prefixes = "http://graaa.org/"));

// emusic player (hide)
register_user_stylesheet(
    make_css_data_uri("#expandTip { display: none !important; }",
                      $domains = "www.emusic.com"));
register_user_stylesheet(
    make_css_data_uri("#player-bar-container { display: none !important; }",
                      $domains = "www.emusic.com"));

// nasa-hsf: make page more printer friendly
register_user_stylesheet(
    make_css_data_uri("img{display: none;}",
                      $url_prefixes = "http://spaceflight1.nasa.gov/realdata/sightings/cities/view.cgi"));

/*
require('selectively-unstyle');

function jjf_kill_google_colors (sheet) {
    for each (let rule in sheet.cssRules) {
        if (rule instanceof Ci.nsIDOMCSSStyleRule)
            rule.style.removeProperty('color');
    }
}

let (google = build_url_regexp(
         $domain = "google",
         $allow_www = true,
         $tlds = ["com", "co.uk", "de", "dk", "es",
                  "fr", "it", "no", "se", "uk"])) {
    selectively_unstyle_alist.push([google, jjf_kill_google_colors]);
};

let (home = get_home_directory()) {
    home.appendRelativePath(".conkerorrc/stylesheets/google-web-search-gray-unimportant.css");
    register_user_stylesheet(make_uri(home).spec);
}
*/

// On some web pages, html input boxes appear smaller than they ought to
// be, not tall enough to fit the height of the text they contain.
// Xulrunner is incorrectly applying quirks mode to these elements,
// causing them to be rendered in an emulation of Internet Explorer's
// notorious flawed box model.  It has been reported that this is related
// to issue 157.  Xulrunner 1.9.0.x has this bug, and to a lesser extent,
// so does Xulrunner 1.9.1.  Here is a work-around that you can put in
// your rc.  This first work-around has the disadvantage that it will
// result in improper sizing of fields which are *supposed* to be in
// quirks mode.
/*
register_user_stylesheet(// heavy-handed approach
    "data:text/css," +
        escape(
            "@namespace url(\"http://www.w3.org/1999/xhtml\");\n" +
            "input:not([type=\"image\"]), textarea {\n"+
            "  -moz-box-sizing: content-box !important;\n"+
            "}"));//*/
/*register_user_stylesheet(// manual approach
    "data:text/css," +
        escape(
            "@namespace url(\"http://www.w3.org/1999/xhtml\");\n"+
            "@-moz-document url-prefix(http://www.naxosmusiclibrary.com/)\n"+
            "{\n"+
            "input:not([type=\"image\"]), textarea {\n"+
            "  -moz-box-sizing: content-box !important;\n"+
            "}}"));//*/
// offending selector: input:not([type="image"])
<commit_msg>Use template strings.<commit_after>// Time-stamp: "2018-12-27 20:06:48 phil"
// Section 7x is for Conkeror internal tweaks (?)
dumpln("72theme");

// Example: darken Astronomy Picture of the Day (APOD)
// register_user_stylesheet(
//     make_css_data_uri(["body{background: black !important; color: grey !important;}",
//                        ":link, :link * {color: #4986dd !important;}",
//                        ":visited, :visited * {color: #d75047 !important;}"],
//                       $url_prefixes = "http://apod.nasa.gov/apod/"));

// Based on http://www.featureblend.com/css-json.html

// With inheritance:
// var cssjson = {
//     ".copy-1":{
//         "font-family":"Verdana, Geneva, Arial, Helvetica, sans-serif",
//         "font-size":"11px",
//         "color":"#CCC"
//     },
//     "div#container div#header":{
//         "CSSJSON-INHERIT-SELECTOR":".copy-1",
//         "position":"absolute",
//         "top":"12px",
//         "left":"4px"
//     }
// }

// TODO write tests
function ph_generate_css (json) {
    var styleStr = "";
    for (var i in json) {
        styleStr += i + " {";
        for (var j in json[i]) {
            if (j == "CSSJSON-INHERIT-SELECTOR") {
                for (var k in json[json[i][j]]) {
                    styleStr += `${k}: "${json[json[i][j]][k]} !important"; `;
                }
            } else {
                styleStr += `${j}: "${json[i][j]} !important"; `;
            }
        }
        styleStr += "} ";
    }
    return styleStr;
}

var ph_dark_css = {
    "*" : {
        "font-style"                : "normal"            ,
        "background"                : "#404040"           ,
        "color"                     : "#cccccc"           ,
        "text-shadow"               : "0px 0px 0px black" ,
        "-moz-border-bottom-colors" : "#505050 #505050"   ,
        "-moz-border-top-colors"    : "#505050 #505050"   ,
        "-moz-border-right-colors"  : "#505050 #505050"   ,
        "-moz-border-left-colors"   : "#505050 #505050"   ,
        "box-shadow"                : "0 0 0 black"       ,
        "-moz-box-shadow"           : "0 0 0 black"       ,
        "border-color"              : "#505050"           ,
        "text-decoration"           : "none"
    },
    "code, pre, code *, pre *" : {
        "color"                     : "#f6f3e8"           ,
        "background-color"          : "#353535"
    },
    ":link, :link *" : {
        "color"                     : "#8ac6f2"
    },
    ":visited, :visited *" : {
        "color"                     : "#95e454"
    },
    "h1, h2, h3, h4, h5, h6" : {
        "color"                     : "white"
    },
    "em" : {
        "background-color"          : "#505050"
    }
};

function darken_page (I) {
    const styles = ph_generate_css(ph_dark_css);
    const document = I.window.buffers.current.document;
    if (document.createStyleSheet) {
        document.createStyleSheet(`javascript:'${styles}'`);
    } else {
        const newSS = document.createElement('link');
        newSS.rel = 'stylesheet';
        newSS.href = `data:text/css,${escape(styles)}`;
        document.getElementsByTagName("head")[0].appendChild(newSS);
    }
}

// function darken_page (I) {
//     var newSS, styles='* { background: #404040 ! important; color: #cccccc !important }'
//         + ':link, :link * { color: #8ac6f2 !important }'
//         + ':visited, :visited * { color: #95e454 !important }';

//     var document = I.window.buffers.current.document;

//     if (document.createStyleSheet) {
//         document.createStyleSheet("javascript:'" + styles + "'");
//     } else {
//         newSS=document.createElement('link');
//         newSS.rel='stylesheet';
//         newSS.href='data:text/css,'+escape(styles);
//         document.getElementsByTagName("head")[0].appendChild(newSS);
//     }
// }

interactive("darken-page", "Darken the page in an attempt to save your eyes.",
            darken_page);

define_key(content_buffer_normal_keymap, "C-M-d", "darken-page");

/*
let (home = get_home_directory()) {
    home.appendRelativePath(".conkerorrc/stylesheets/facebook-hello-kitty.css");
    register_user_stylesheet(make_uri(home).spec);
}
*/

// apod: darken
register_user_stylesheet(
    make_css_data_uri(["body{background: black !important; color: grey !important;}",
                       ":link, :link * {color: #4986dd !important;}",
                       ":visited, :visited * {color: #d75047 !important;}"],
                      $url_prefixes = "http://apod.nasa.gov/apod/"));

// conkerorwiki
register_user_stylesheet(
    make_css_data_uri(
        ["html, body { background-color: #666 !important; }",
         "#page { padding: 2px 0px !important; }",
         "#content { padding: 0px 20px; }",
         "#header, #page, #footer { max-width: 65em; background-color: white; }"],
        $url_prefixes = "http://conkeror.org"));

// graaa fix background
register_user_stylesheet(
    make_css_data_uri("body {background-color:black !important; color: white;}",
                      $url_prefixes = "http://graaa.org/"));

// emusic player (hide)
register_user_stylesheet(
    make_css_data_uri("#expandTip { display: none !important; }",
                      $domains = "www.emusic.com"));
register_user_stylesheet(
    make_css_data_uri("#player-bar-container { display: none !important; }",
                      $domains = "www.emusic.com"));

// nasa-hsf: make page more printer friendly
register_user_stylesheet(
    make_css_data_uri("img{display: none;}",
                      $url_prefixes = "http://spaceflight1.nasa.gov/realdata/sightings/cities/view.cgi"));

/*
require('selectively-unstyle');

function jjf_kill_google_colors (sheet) {
    for each (let rule in sheet.cssRules) {
        if (rule instanceof Ci.nsIDOMCSSStyleRule)
            rule.style.removeProperty('color');
    }
}

let (google = build_url_regexp(
         $domain = "google",
         $allow_www = true,
         $tlds = ["com", "co.uk", "de", "dk", "es",
                  "fr", "it", "no", "se", "uk"])) {
    selectively_unstyle_alist.push([google, jjf_kill_google_colors]);
};

let (home = get_home_directory()) {
    home.appendRelativePath(".conkerorrc/stylesheets/google-web-search-gray-unimportant.css");
    register_user_stylesheet(make_uri(home).spec);
}
*/

// On some web pages, html input boxes appear smaller than they ought to
// be, not tall enough to fit the height of the text they contain.
// Xulrunner is incorrectly applying quirks mode to these elements,
// causing them to be rendered in an emulation of Internet Explorer's
// notorious flawed box model.  It has been reported that this is related
// to issue 157.  Xulrunner 1.9.0.x has this bug, and to a lesser extent,
// so does Xulrunner 1.9.1.  Here is a work-around that you can put in
// your rc.  This first work-around has the disadvantage that it will
// result in improper sizing of fields which are *supposed* to be in
// quirks mode.
/*
register_user_stylesheet(// heavy-handed approach
    "data:text/css," +
        escape(
            "@namespace url(\"http://www.w3.org/1999/xhtml\");\n" +
            "input:not([type=\"image\"]), textarea {\n"+
            "  -moz-box-sizing: content-box !important;\n"+
            "}"));//*/
/*register_user_stylesheet(// manual approach
    "data:text/css," +
        escape(
            "@namespace url(\"http://www.w3.org/1999/xhtml\");\n"+
            "@-moz-document url-prefix(http://www.naxosmusiclibrary.com/)\n"+
            "{\n"+
            "input:not([type=\"image\"]), textarea {\n"+
            "  -moz-box-sizing: content-box !important;\n"+
            "}}"));//*/
// offending selector: input:not([type="image"])
<|endoftext|>