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

/**
 * @fileoverview Colour blocks for Blockly.
 * @author fraser@google.com (Neil Fraser)
 */
'use strict';

goog.provide('Blockly.Blocks.colour');

/**
 * Common HSV hue for all blocks in this category.
 */
Blockly.Blocks.colour.HUE = 20;

Blockly.Blocks['colour_picker'] = {
  /**
   * Block for colour picker.
   * @this Blockly.Block
   */
  init: function() {
    this.jsonInit({
      "message0": "%1",
      "args0": [
        {
          "type": "field_colour",
          "name": "COLOUR",
          "colour": "#ff0000"
        }
      ],
      "output": "Colour",
      "colour": Blockly.Blocks.colour.HUE,
      "helpUrl": Blockly.Msg.COLOUR_PICKER_HELPURL
    });
    // Assign 'this' to a variable for use in the tooltip closure below.
    var thisBlock = this;
    // Colour block is trivial.  Use tooltip of parent block if it exists.
    this.setTooltip(function() {
      var parent = thisBlock.getParent();
      return (parent && parent.tooltip) || Blockly.Msg.COLOUR_PICKER_TOOLTIP;
    });
  }
};

Blockly.Blocks['colour_random'] = {
  /**
   * Block for random colour.
   * @this Blockly.Block
   */
  init: function() {
    this.jsonInit({
      "message0": Blockly.Msg.COLOUR_RANDOM_TITLE,
      "output": "Colour",
      "colour": Blockly.Blocks.colour.HUE,
      "tooltip": Blockly.Msg.COLOUR_RANDOM_TOOLTIP,
      "helpUrl": Blockly.Msg.COLOUR_RANDOM_HELPURL
    });
  }
};

Blockly.Blocks['colour_rgb'] = {
  /**
   * Block for composing a colour from RGB components.
   * @this Blockly.Block
   */
  init: function() {
    this.setHelpUrl(Blockly.Msg.COLOUR_RGB_HELPURL);
    this.setColour(Blockly.Blocks.colour.HUE);
    this.appendValueInput('RED')
        .setCheck('Number')
        .setAlign(Blockly.ALIGN_RIGHT)
        .appendField(Blockly.Msg.COLOUR_RGB_TITLE)
        .appendField(Blockly.Msg.COLOUR_RGB_RED);
    this.appendValueInput('GREEN')
        .setCheck('Number')
        .setAlign(Blockly.ALIGN_RIGHT)
        .appendField(Blockly.Msg.COLOUR_RGB_GREEN);
    this.appendValueInput('BLUE')
        .setCheck('Number')
        .setAlign(Blockly.ALIGN_RIGHT)
        .appendField(Blockly.Msg.COLOUR_RGB_BLUE);
    this.setOutput(true, 'Colour');
    this.setTooltip(Blockly.Msg.COLOUR_RGB_TOOLTIP);
  }
};

Blockly.Blocks['colour_blend'] = {
  /**
   * Block for blending two colours together.
   * @this Blockly.Block
   */
  init: function() {
    this.setHelpUrl(Blockly.Msg.COLOUR_BLEND_HELPURL);
    this.setColour(Blockly.Blocks.colour.HUE);
    this.appendValueInput('COLOUR1')
        .setCheck('Colour')
        .setAlign(Blockly.ALIGN_RIGHT)
        .appendField(Blockly.Msg.COLOUR_BLEND_TITLE)
        .appendField(Blockly.Msg.COLOUR_BLEND_COLOUR1);
    this.appendValueInput('COLOUR2')
        .setCheck('Colour')
        .setAlign(Blockly.ALIGN_RIGHT)
        .appendField(Blockly.Msg.COLOUR_BLEND_COLOUR2);
    this.appendValueInput('RATIO')
        .setCheck('Number')
        .setAlign(Blockly.ALIGN_RIGHT)
        .appendField(Blockly.Msg.COLOUR_BLEND_RATIO);
    this.setOutput(true, 'Colour');
    this.setTooltip(Blockly.Msg.COLOUR_BLEND_TOOLTIP);
  }
};
<commit_msg>Update colour.js<commit_after>
'use strict';

goog.provide('Blockly.Blocks.colour');

/**
 * Common HSV hue for all blocks in this category.
 */
Blockly.Blocks.colour.HUE = 20;

Blockly.Blocks['colour_picker'] = {
  /**
   * Block for colour picker.
   * @this Blockly.Block
   */
  init: function() {
    this.jsonInit({
      "message0": "%1",
      "args0": [
        {
          "type": "field_colour",
          "name": "COLOUR",
          "colour": "#ff0000"
        }
      ],
      "output": "Colour",
      "colour": Blockly.Blocks.colour.HUE,
      "helpUrl": Blockly.Msg.COLOUR_PICKER_HELPURL
    });
    // Assign 'this' to a variable for use in the tooltip closure below.
    var thisBlock = this;
    // Colour block is trivial.  Use tooltip of parent block if it exists.
    this.setTooltip(function() {
      var parent = thisBlock.getParent();
      return (parent && parent.tooltip) || Blockly.Msg.COLOUR_PICKER_TOOLTIP;
    });
  }
};

Blockly.Blocks['colour_random'] = {
  /**
   * Block for random colour.
   * @this Blockly.Block
   */
  init: function() {
    this.jsonInit({
      "message0": Blockly.Msg.COLOUR_RANDOM_TITLE,
      "output": "Colour",
      "colour": Blockly.Blocks.colour.HUE,
      "tooltip": Blockly.Msg.COLOUR_RANDOM_TOOLTIP,
      "helpUrl": Blockly.Msg.COLOUR_RANDOM_HELPURL
    });
  }
};

Blockly.Blocks['colour_rgb'] = {
  /**
   * Block for composing a colour from RGB components.
   * @this Blockly.Block
   */
  init: function() {
    this.setHelpUrl(Blockly.Msg.COLOUR_RGB_HELPURL);
    this.setColour(Blockly.Blocks.colour.HUE);
    this.appendValueInput('RED')
        .setCheck('Number')
        .setAlign(Blockly.ALIGN_RIGHT)
        .appendField(Blockly.Msg.COLOUR_RGB_TITLE)
        .appendField(Blockly.Msg.COLOUR_RGB_RED);
    this.appendValueInput('GREEN')
        .setCheck('Number')
        .setAlign(Blockly.ALIGN_RIGHT)
        .appendField(Blockly.Msg.COLOUR_RGB_GREEN);
    this.appendValueInput('BLUE')
        .setCheck('Number')
        .setAlign(Blockly.ALIGN_RIGHT)
        .appendField(Blockly.Msg.COLOUR_RGB_BLUE);
    this.setOutput(true, 'Colour');
    this.setTooltip(Blockly.Msg.COLOUR_RGB_TOOLTIP);
  }
};

Blockly.Blocks['colour_blend'] = {
  /**
   * Block for blending two colours together.
   * @this Blockly.Block
   */
  init: function() {
    this.setHelpUrl(Blockly.Msg.COLOUR_BLEND_HELPURL);
    this.setColour(Blockly.Blocks.colour.HUE);
    this.appendValueInput('COLOUR1')
        .setCheck('Colour')
        .setAlign(Blockly.ALIGN_RIGHT)
        .appendField(Blockly.Msg.COLOUR_BLEND_TITLE)
        .appendField(Blockly.Msg.COLOUR_BLEND_COLOUR1);
    this.appendValueInput('COLOUR2')
        .setCheck('Colour')
        .setAlign(Blockly.ALIGN_RIGHT)
        .appendField(Blockly.Msg.COLOUR_BLEND_COLOUR2);
    this.appendValueInput('RATIO')
        .setCheck('Number')
        .setAlign(Blockly.ALIGN_RIGHT)
        .appendField(Blockly.Msg.COLOUR_BLEND_RATIO);
    this.setOutput(true, 'Colour');
    this.setTooltip(Blockly.Msg.COLOUR_BLEND_TOOLTIP);
  }
};
<|endoftext|>


<commit_before>//Code attributed to Peter Herrmann: https://github.com/peterherrmann/BetterLog
// No license specified in the github repository
// No changes made to lib by current author

/*************************************************************************
* Globals
*********/
var sheet_; //the spreadsheet that is appended to
var SHEET_MAX_ROWS = 50000; //sheet is cleared and starts again
var SHEET_LOG_CELL_WIDTH = 1000; //
var SHEET_LOG_HEADER = 'Message layout: Date Time UTC-Offset MillisecondsSinceInvoked LogLevel Message. Use Ctrl↓ (or Command↓) to jump to the last row';
var DATE_TIME_LAYOUT = 'yyyy-MM-dd HH:mm:ss:SSS Z'; //http://docs.oracle.com/javase/6/docs/api/java/text/SimpleDateFormat.html

//ref http://docs.oracle.com/javase/7/docs/api/java/util/logging/Level.html
var Level = Object.freeze({
  OFF:    Number.MAX_VALUE,
  SEVERE: 1000,
  WARNING:900,
  INFO:   800,
  CONFIG: 700,
  FINE:   500,
  FINER:  400,
  FINEST: 300,
  ALL: Number.MIN_VALUE});

var level_ = Level.INFO; //set as default. The log level. We log everything this level or greater.
var startTime = new Date();
var thisApp_ = this;
var counter = 0;

/*************************************************************************
* public methods
*********/

/**
* Allows logging to a Google spreadsheet.
*
* @param  {String} optKey    The spreadsheet key (optional). Defaults to the active spreadsheet if available.
* @param  {String} optSheetName The name of the sheet (optional). Defaults to "Log". The sheet is created if needed.
* @returns {BetterLog} this object, for chaining
*/
function useSpreadsheet(optKey, optSheetName) {
  setLogSheet_(optKey, optSheetName);
  sheet_.getRange(1,1).setValue(SHEET_LOG_HEADER); //in case we need to update
  rollLogOver_(); //rollover the log if we need to
  return thisApp_;
}

/**
* Logs at the SEVERE level. SEVERE is a message level indicating a serious failure.
* In general SEVERE messages should describe events that are of considerable importance and
* which will prevent normal program execution. They should be reasonably intelligible to end users and to system administrators.
*
* @param  {Object} message    The message to log or an sprintf-like format string (uses Utilities.formatString() internally - see http://www.perlmonks.org/?node_id=20519 as a good reference).
* @param  {Object...} optValues  If a format string is used in the message, a number of values to insert into the format string.
* @returns {BetterLog} this object, for chaining
*/
function severe(message, optValues) {
  var lev = Level.SEVERE;
  if (isLoggable_(lev)) {
    log_({"message": (typeof message == 'string' || message instanceof String) ? Utilities.formatString.apply(this, arguments) : message,
          "level": lev,
          "time": new Date(),
          "elapsedTime": getElapsedTime_()
         });
  }
  return thisApp_;
}

/**
* Logs at the WARNING level. WARNING is a message level indicating a potential problem.
* In general WARNING messages should describe events that will be of interest to end users
* or system managers, or which indicate potential problems.
*
* @param  {Object} message    The message to log or an sprintf-like format string (uses Utilities.formatString() internally - see http://www.perlmonks.org/?node_id=20519 as a good reference).
* @param  {Object...} optValues  If a format string is used in the message, a number of values to insert into the format string.
* @returns {BetterLog} this object, for chaining
*/
function warning(message, optValues) {
  var lev = Level.WARNING;
  if (isLoggable_(lev)) {
    log_({"message": (typeof message == 'string' || message instanceof String) ? Utilities.formatString.apply(this, arguments) : message,
          "level": lev,
          "time": new Date(),
          "elapsedTime": getElapsedTime_()
         });
  }
  return thisApp_;
}

/**
* Logs at the INFO level. INFO is a message level for informational messages.
* Typically INFO messages will be written to the console or its equivalent. So the INFO level
* should only be used for reasonably significant messages that will make sense to end users and system administrators.

Examples:

function myFunction() {
  //Best practice for using BetterLog and logging to a spreadsheet:
  // You can add and set the property "BetterLogLevel" in File > Project Properties and change it to
  // "OFF","SEVERE","WARNING","INFO","CONFIG","FINE","FINER","FINEST" or "ALL" at runtime without editing code.
   Logger = BetterLog.setLevel(ScriptProperties.getProperty('BetterLogLevel')) //defaults to 'INFO' level
  .useSpreadsheet('0AhDqyd_bUCmvdDdGczRlX00zUlBMeGNLeE9SNlJ0VGc'); //automatically rolls over at 50,000 rows

  Logger.log('Messages using Logger.log continue to work');

  Logger.config('The current log level is %s', Logger.getLevel());
  Logger.finer('Entering the "%s" function', arguments.callee.name); //only logged if level is FINER, FINEST or ALL.

  Logger.info('Starting my function that does stuff');

  //Do our work
  for (var i = 0; i < 5; i++) {
    //do detailed stuff
    Logger.finest('Inside the for loop that does the xyz work. i is currently: %d', i);
  }

  Logger.info('My work is complete and I performed %d iterations', i);
  Logger.finer('Returning from the "%s" function', arguments.callee.name);
}

*
* @param  {Object} message    The message to log or an sprintf-like format string (uses Utilities.formatString() internally - see http://www.perlmonks.org/?node_id=20519 as a good reference).
* @param  {Object...} optValues  If a format string is used in the message, a number of values to insert into the format string.
* @returns {BetterLog} this object, for chaining
*/
function info(message, optValues) {
  var lev = Level.INFO;
  if (isLoggable_(lev)) {
    log_({"message": (typeof message == 'string' || message instanceof String) ? Utilities.formatString.apply(this, arguments) : message,
          "level": lev,
          "time": new Date(),
          "elapsedTime": getElapsedTime_()
         });
  }
  return thisApp_;
}

/**
* Logs at the CONFIG level. CONFIG is a message level for static configuration messages.
* CONFIG messages are intended to provide a variety of static configuration information,
* to assist in debugging problems that may be associated with particular configurations.
*
* @param  {Object} message    The message to log or an sprintf-like format string (uses Utilities.formatString() internally - see http://www.perlmonks.org/?node_id=20519 as a good reference).
* @param  {Object...} optValues  If a format string is used in the message, a number of values to insert into the format string.
* @returns {BetterLog} this object, for chaining
*/
function config(message, optValues) {
  var lev = Level.CONFIG;
  if (isLoggable_(lev)) {
    log_({"message": (typeof message == 'string' || message instanceof String) ? Utilities.formatString.apply(this, arguments) : message,
          "level": lev,
          "time": new Date(),
          "elapsedTime": getElapsedTime_()
         });
  }
  return thisApp_;
}

/**
* Logs at the FINE level. FINE is a message level providing tracing information.
* All of FINE, FINER, and FINEST are intended for relatively detailed tracing.
* The exact meaning of the three levels will vary between subsystems, but in general,
* FINEST should be used for the most voluminous detailed output,
* FINER for somewhat less detailed output, and FINE for the lowest volume (and most important) messages.
*
* In general the FINE level should be used for information that will be broadly interesting to developers
* who do not have a specialized interest in the specific subsystem.
* FINE messages might include things like minor (recoverable) failures. Issues indicating potential performance problems are also worth logging as FINE. T
*
* @param  {Object} message    The message to log or an sprintf-like format string (uses Utilities.formatString() internally - see http://www.perlmonks.org/?node_id=20519 as a good reference).
* @param  {Object...} optValues  If a format string is used in the message, a number of values to insert into the format string.
* @returns {BetterLog} this object, for chaining
*/
function fine(message, optValues) {
  var lev = Level.FINE;
  if (isLoggable_(lev)) {
    log_({"message": (typeof message == 'string' || message instanceof String) ? Utilities.formatString.apply(this, arguments) : message,
          "level": lev,
          "time": new Date(),
          "elapsedTime": getElapsedTime_()
         });
  }
  return thisApp_;
}

/**
* Logs at the FINER level. FINER indicates a fairly detailed tracing message.
* By default logging calls for entering, returning, or throwing an exception are traced at this level.
*
* @param  {Object} message    The message to log or an sprintf-like format string (uses Utilities.formatString() internally - see http://www.perlmonks.org/?node_id=20519 as a good reference).
* @param  {Object...} optValues  If a format string is used in the message, a number of values to insert into the format string.
* @returns {BetterLog} this object, for chaining
*/
function finer(message, optValues) {
  var lev = Level.FINER;
  if (isLoggable_(lev)) {
    log_({"message": (typeof message == 'string' || message instanceof String) ? Utilities.formatString.apply(this, arguments) : message,
          "level": lev,
          "time": new Date(),
          "elapsedTime": getElapsedTime_()
         });
  }
  return thisApp_;
}

/**
* Logs at the FINEST level. FINEST indicates a highly detailed tracing message.
*
* @param  {Object} message    The message to log or an sprintf-like format string (uses Utilities.formatString() internally - see http://www.perlmonks.org/?node_id=20519 as a good reference).
* @param  {Object...} optValues  If a format string is used in the message, a number of values to insert into the format string.
* @returns {BetterLog} this object, for chaining
*/
function finest(message, optValues) {
  var lev = Level.FINEST;
  if (isLoggable_(lev)) {
    log_({"message": (typeof message == 'string' || message instanceof String) ? Utilities.formatString.apply(this, arguments) : message,
          "level": lev,
          "time": new Date(),
          "elapsedTime": getElapsedTime_()
         });
  }
  return thisApp_;
}

/**
* Logs at the INFO level. INFO is a message level for informational messages.
* Typically INFO messages will be written to the console or its equivalent. So the INFO level should
* only be used for reasonably significant messages that will make sense to end users and system administrators.
*
* @param  {Object} message    The message to log or an sprintf-like format string (uses Utilities.formatString() internally - see http://www.perlmonks.org/?node_id=20519 as a good reference).
* @param  {Object...} optValues  If a format string is used in the message, a number of values to insert into the format string.
* @returns {BetterLog} this object, for chaining
*/
function log(message, optValues) {
  return info.apply(this, arguments);
}

/**
* Sets the new log level
*
* @param  {String} logLevel    The new log level e.g. "OFF","SEVERE","WARNING","INFO","CONFIG","FINE","FINER","FINEST" or "ALL".
* @returns {BetterLog} this object, for chaining
*/
function setLevel(logLevel) {
  if (typeof logLevel === "string") {
    var logLevel = stringToLevel_(logLevel);
  }
  if (logLevel != getLevel_()) {
    setLevel_(logLevel);
  }
  return thisApp_;
}
/**
* Gets the current log level name
*
* @returns {String} The name of the current log level e.g. "OFF","SEVERE","WARNING","INFO","CONFIG","FINE","FINER","FINEST" or "ALL".
*/
function getLevel() {
  return levelToString_(getLevel_());
}

/*************************************************************************
* @private functions
********************/

// Returns the string as a Level.
function stringToLevel_(str) {
  for (var name in Level) {
    if (name == str) {
      return Level[name];
    }
  }
}

// Returns the Level as a String
function levelToString_(lvl) {
  for (var name in Level) {
    if (Level[name] == lvl)
      return name;
  }
}

//gets the current logging level
function getLevel_() {
  return level_;
}

//sets the current logging level
function setLevel_(lvl) {
  for (var name in Level) {
    if (Level[name] == lvl) {
      level_ = lvl;
      info("Log level has been set to " +  getLevel());
      break;
    }
  }
}

//checks to see if this level is enabled
function isLoggable_(Level) {
  if (getLevel_()<=Level) {
    return true;
  }
  return false;
}

//core logger function
function log_(msg) {
  counter++;
  //default console logging (built in with Google Apps Script's View > Logs...)
  Logger.log(convertUsingDefaultPatternLayout_(msg));
  //ss logging
  if (sheet_) {
    logToSheet_(msg);
  }
}

//  rolls over the log if we need to
function rollLogOver_() {
  var rowCount = sheet_.getLastRow();
  if (rowCount > SHEET_MAX_ROWS) {
    //copy the log
    var ss = sheet_.getParent();
    var oldLog = ss.copy(ss.getName() + ' as at ' + Utilities.formatDate(new Date(), Session.getScriptTimeZone(), DATE_TIME_LAYOUT));
    //add current viewers and editors to old log
    oldLog.addViewers(ss.getViewers());
    oldLog.addEditors(ss.getEditors());
    // prep the live log
    sheet_.deleteRows(2, sheet_.getMaxRows()-2);
    sheet_.getRange(1,1).setValue(SHEET_LOG_HEADER);
    //sheet_.appendRow(['Log reached ' + rowCount + ' rows (MAX_ROWS is ' + SHEET_MAX_ROWS + ') and was cleared. Previous log is available here:']);
    sheet_.getRange("A2").setValue(['Log reached ' + rowCount + ' rows (MAX_ROWS is ' + SHEET_MAX_ROWS + ') and was cleared. Previous log is available here:']);
    sheet_.appendRow([oldLog.getUrl()]);
  }
}

//logs to spreadsheet
function logToSheet_(msg) {
  //check for rollover every 100 rows logged during one invocation
  if (counter % 100 === 0) {
    rollLogOver_();
  }
  //sheet_.appendRow([convertUsingSheetPatternLayout_(msg)]);
  call_(function() {sheet_.appendRow([convertUsingSheetPatternLayout_(msg)]);});
}
// convert message to text string
function convertUsingDefaultPatternLayout_(msg) {
  var dt = Utilities.formatDate(msg.time, Session.getScriptTimeZone(), DATE_TIME_LAYOUT);
  var message = dt + " " + pad_(msg.elapsedTime,6) + " " + levelToString_(msg.level) + " " + msg.message;
  return message;
}
// convert message to text string
function convertUsingSheetPatternLayout_(msg) {
  return convertUsingDefaultPatternLayout_(msg);
}
//Sets the log sheet, creating one if it doesn't exist
function setLogSheet_(optKey, optSheetName) {
  var sheetName = optSheetName || "Log";
  var ss = (optKey) ? SpreadsheetApp.openById(optKey) : SpreadsheetApp.getActiveSpreadsheet();
  var sheets = call_(function() {return ss.getSheets();});
  for (var i = 0; i < sheets.length; i++) {
    if (sheets[i].getName() === sheetName) {
      sheet_ = sheets[i];
      return;
    }
  }
  sheet_ = ss.insertSheet(sheetName, i);
  sheet_.deleteColumns(2,sheet_.getMaxColumns()-1);
  sheet_.getRange(1,1).setValue(SHEET_LOG_HEADER);
  sheet_.setFrozenRows(1);
  sheet_.setColumnWidth(1, SHEET_LOG_CELL_WIDTH);
  info("Log created");
}

//gets the time since the start of logging
function getElapsedTime_(){
  return (new Date() - startTime); //milliseconds
}
// pads a number with leading zeros
function pad_(n,len) {
  var s = n.toString();
  if (s.length < len) {
    s = ('0000000000' + s).slice(-len);
  }
  return s;
}
function getFormattedStringnewish_(args) {
  if (args.length === 1) {
    return args[0];
  }
  var afunction = Utilities.formatString;
  return afunction.apply(this, arguments);
}
function getFormattedString_(args) {
  //http://stackoverflow.com/q/15913071/298650
  if (args.length === 1) return args[0];
  for (var i = 0, arr = []; i < args.length; ++i) {
    args[i] = (args[i] === '')? args[i] : args[i]||'undefined'; //undefined become strings 'undefined'
    arr.push('args[' + i + ']');
  }
  var result = eval('Utilities.formatString(' + arr.join() + ')');
  return result;
}
function getFormattedStringA_(args) {
  //(typeof message == 'string' || message instanceof String) : Utilities.formatString.apply(this, arguments),
  /*if (args.length === 1) {
    return args[0];
  }*/

  return Utilities.formatString.apply(this, arguments);
}
function test(message, optValues) {
  var lev = Level.INFO;
  if (isLoggable_(lev)) {
    log_({"message": (typeof message == 'string' || message instanceof String) ? Utilities.formatString.apply(this, arguments) : message,
          "level": lev,
          "time": new Date(),
          "elapsedTime": getElapsedTime_()
         });
  }
  return thisApp_;
}

//copy version 10 lib GASRetry 'MGJu3PS2ZYnANtJ9kyn2vnlLDhaBgl_dE' (changed function name and log line)
function call_(func, optLoggerFunction) {
  for (var n=0; n<6; n++) {
    try {
      return func();
    } catch(e) {
      if (optLoggerFunction) {optLoggerFunction("call_ " + n + ": " + e)}
      if (n == 5) {
        throw e;
      }
      Utilities.sleep((Math.pow(2,n)*1000) + (Math.round(Math.random() * 1000)));
    }
  }
}<commit_msg>configuring BetterLog to run directly within the script (as opposed to an imported lib).<commit_after>// original BetterLogger library attributed to Peter Herrmann: https://github.com/peterherrmann/BetterLog
// No license specified in the github repository
// Changes made to lib by Will Patera

var BetterLogLevels = {
  CRITICAL: 50,
  ERROR: 40,
  WARNING: 30,
  INFO: 20,
  DEBUG: 10,
  NOTSET: 0
};

var BetterLog = {
  // globals
  sheet: false,
  SHEET_MAX_ROWS: 50000, //sheet is cleared and starts again
  SHEET_LOG_CELL_WIDTH: 1000, //
  SHEET_LOG_HEADER: 'Message layout: Date Time UTC-Offset MillisecondsSinceInvoked LogLevel Message. Use Ctrl↓ (or Command↓) to jump to the last row',
  DATE_TIME_LAYOUT: 'yyyy-MM-dd HH:mm:ss:SSS Z', //http://docs.oracle.com/javase/6/docs/api/java/text/SimpleDateFormat.html
  levels: BetterLogLevels,
  level: BetterLogLevels.INFO, //default to debug
  startTime: new Date(),
  counter: 0,

  // public methods
  useSpreadsheet: function(optKey, optSheetName){
    this.setLogSheet_(optKey,optSheetName);
    this.sheet.getRange(1,1).setValue(this.SHEET_LOG_HEADER);
    this.rollLogOver_();
    return this;
  },

  ////////////////////////////////////////////////////////////////////////////////////////////////
  // logger methods
  ////////////////////////////////////////////////////////////////////////////////////////////////
  critical: function(message,optValues){
    var currentLevel = BetterLogLevels.CRITICAL;
    if(this.level <= currentLevel){
      this.log_({"message": (typeof message == 'string' || message instanceof String) ? Utilities.formatString.apply(this, arguments) : message,
            "level": currentLevel,
            "time": new Date(),
            "elapsedTime": this.getElapsedTime_()
           });
    }
    return this;
  },

  error: function(message,optValues){
    var currentLevel = BetterLogLevels.ERROR;
    if(this.level <= currentLevel){
      this.log_({"message": (typeof message == 'string' || message instanceof String) ? Utilities.formatString.apply(this, arguments) : message,
            "level": currentLevel,
            "time": new Date(),
            "elapsedTime": this.getElapsedTime_()
           });
    }
    return this;
  },

  warning: function(message,optValues){
    var currentLevel = BetterLogLevels.WARNING;
    if(this.level <= currentLevel){
      this.log_({"message": (typeof message == 'string' || message instanceof String) ? Utilities.formatString.apply(this, arguments) : message,
            "level": currentLevel,
            "time": new Date(),
            "elapsedTime": this.getElapsedTime_()
           });
    }
    return this;
  },

  info: function(message,optValues){
    var currentLevel = BetterLogLevels.INFO;
    if(this.level <= currentLevel){
      this.log_({"message": (typeof message == 'string' || message instanceof String) ? Utilities.formatString.apply(this, arguments) : message,
            "level": currentLevel,
            "time": new Date(),
            "elapsedTime": this.getElapsedTime_()
           });
    }
    return this;
  },

  debug: function(message,optValues){
    var currentLevel = BetterLogLevels.DEBUG;
    if(this.level <= currentLevel){
      this.log_({"message": (typeof message == 'string' || message instanceof String) ? Utilities.formatString.apply(this, arguments) : message,
            "level": currentLevel,
            "time": new Date(),
            "elapsedTime": this.getElapsedTime_()
           });
    }
    return this;
  },

  log: function(message, optValues) {
    var this_ = this;
    return this.info.apply(this_, arguments);
  },
  ////////////////////////////////////////////////////////////////////////////////////////////////
  // private class only methods
  ////////////////////////////////////////////////////////////////////////////////////////////////

  // core logging function
  log_: function(msg){
    this.counter++;
    // Logger.log(this.convertUsingDefaultPatternLayout_(msg));
    if (this.sheet) {
      this.logToSheet_(msg);
    }
  },

  stringToLevel_: function(str) {
    for (var name in this.levels) {
      if (name == str) {
        return this.levels[name];
      }
    }
  },

  // Returns the Level as a String
  levelToString_: function(lvl) {
    for (var name in this.levels) {
      if (this.levels[name] == lvl)
        return name;
    }
  },

  //  rolls over the log if we need to
  rollLogOver_: function() {
    var rowCount = this.sheet.getLastRow();
    if (rowCount > this.SHEET_MAX_ROWS) {
      //copy the log
      var ss = this.sheet.getParent();
      var oldLog = ss.copy(ss.getName() + ' as at ' + Utilities.formatDate(new Date(), Session.getScriptTimeZone(), this.DATE_TIME_LAYOUT));
      //add current viewers and editors to old log
      oldLog.addViewers(ss.getViewers());
      oldLog.addEditors(ss.getEditors());
      // prep the live log
      this.sheet.deleteRows(2, this.sheet.getMaxRows()-2);
      this.sheet.getRange(1,1).setValue(this.SHEET_LOG_HEADER);
      this.sheet.getRange("A2").setValue(['Log reached ' + rowCount + ' rows (MAX_ROWS is ' + this.SHEET_MAX_ROWS + ') and was cleared. Previous log is available here:']);
      this.sheet.appendRow([oldLog.getUrl()]);
    }
  },

  //logs to spreadsheet
  logToSheet_: function(msg) {
    //check for rollover every 100 rows logged during one invocation
    if (this.counter % 100 === 0) {
      this.rollLogOver_();
    }
    var s = this.sheet;
    msgConverted = this.convertUsingSheetPatternLayout_(msg);
    this.call_(function() {s.appendRow([msgConverted]);});
  },

  // convert message to text string
  convertUsingDefaultPatternLayout_: function(msg) {
    var dt = Utilities.formatDate(msg.time, Session.getScriptTimeZone(), this.DATE_TIME_LAYOUT);
    var message = dt + " " + this.pad_(msg.elapsedTime,6) + " " + this.levelToString_(msg.level) + " " + msg.message;
    return message;
  },

  // convert message to text string
  convertUsingSheetPatternLayout_: function(msg) {
    return this.convertUsingDefaultPatternLayout_(msg);
  },

  //Sets the log sheet, creating one if it doesn't exist
  setLogSheet_: function(optKey, optSheetName) {
    var sheetName = optSheetName || "Log";
    var ss = (optKey) ? SpreadsheetApp.openById(optKey) : SpreadsheetApp.getActiveSpreadsheet();
    var sheets = this.call_(function() {return ss.getSheets();});
    for (var i = 0; i < sheets.length; i++) {
      if (sheets[i].getName() === sheetName) {
        this.sheet = sheets[i];
        return;
      }
    }
    this.sheet = ss.insertSheet(sheetName, i);
    this.sheet.deleteColumns(2,this.sheet.getMaxColumns()-1);
    this.sheet.getRange(1,1).setValue(this.SHEET_LOG_HEADER);
    this.sheet.setFrozenRows(1);
    this.sheet.setColumnWidth(1, this.SHEET_LOG_CELL_WIDTH);
    this.info("Log created");
  },

  //gets the time since the start of logging
  getElapsedTime_: function(){
    return (new Date() - this.startTime); //milliseconds
  },

  // pads a number with leading zeros
  pad_: function(n,len) {
    var s = n.toString();
    if (s.length < len) {
      s = ('0000000000' + s).slice(-len);
    }
    return s;
  },

  //copy version 10 lib GASRetry 'MGJu3PS2ZYnANtJ9kyn2vnlLDhaBgl_dE' (changed function name and log line)
  call_: function(func, optLoggerFunction) {
    for (var n=0; n<6; n++) {
      try {
        return func();
      } catch(e) {
        if (optLoggerFunction) {optLoggerFunction("call_ " + n + ": " + e);}
        if (n == 5) {
          throw e;
        }
        Utilities.sleep((Math.pow(2,n)*1000) + (Math.round(Math.random() * 1000)));
      }
    }
  }
};<|endoftext|>


<commit_before>var StepList = require('../../step-list');

var startServerInstance = require('../../server-instance');
var server = startServerInstance(beginCommand);

function beginCommand() {

	function beGone() {
		server.stop(function() {
			console.log('done.');
			process.exit();
		});
	}

	var setName = 'example';
	var setData = {
		name: setName,
		swing: 0.5,
		bpm: 130
	};
	var poolEntry1Data = {
		name: 'pool-entry-1',
		volume: 1,
		sampleType: 'local',
		sampleId: '1234-5678'
	};
	var poolEntry2Data = {
		name: 'pool-entry-2',
		volume: 0.8,
		sampleType: 'local',
		sampleId: '0123-4567'
	};
	var pattern1Data = {
		name: 'pattern-1',
		length: 16,
		locked: false
	};
	var pattern2Data = {
		name: 'pattern-2',
		length: 16,
		locked: false
	};
	var patternRow1Data = {
		poolEntry: 'pool-entry-1',
		steps: [1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0]
	};
	var patternRow2Data = {
		poolEntry: 'pool-entry-2',
		steps: [0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0]
	};
	var songData = {
		name: 'song-1',
		locked: false
	};
	var songRow1Data = {
		pattern_id: 'pattern-1',
		offset: 0,
		len: 16,
		count: 2
	};
	var songRow2Data = {
		pattern_id: 'pattern-2',
		offset: 0,
		len: 16,
		count: 1
	};

	var stepList = new StepList();

	var pattern1Id, pattern2Id, songId;

	stepList.addStep(function(cb) {
		server.inject({method: 'get', url: '/api/set/' + setName}, function(result) { cb(); });
	});
	stepList.addStep(function(cb) {
		server.inject({method: 'post', url: '/api/set/' + setName + '/pattern', payload: pattern1Data}, function(res) {
			pattern1Id = res.result.id;
			cb();
		});
	});
	stepList.addStep(function(cb) {
		server.inject({method: 'post', url: '/api/set/' + setName + '/pattern', payload: pattern2Data}, function(res) {
			pattern2Id = res.result.id;
			cb();
		});
	});
	stepList.addStep(function(cb) {
		server.inject({method: 'post', url: '/api/set/' + setName + '/pattern/' + pattern1Id + 'patternrow', payload: patternRow1Data}, function(result) { cb(); });
	});
	stepList.addStep(function(cb) {
		server.inject({method: 'post', url: '/api/set/' + setName + '/pattern/' + pattern1Id + 'patternrow', payload: patternRow2Data}, function(result) { cb(); });
	});
	stepList.addStep(function(cb) {
		server.inject({method: 'post', url: '/api/set/' + setName + '/pattern/' + pattern2Id + 'patternrow', payload: patternRow1Data}, function(result) { cb(); });
	});
	stepList.addStep(function(cb) {
		server.inject({method: 'post', url: '/api/set/' + setName + '/pattern/' + pattern2Id + 'patternrow', payload: patternRow2Data}, function(result) { cb(); });
	});
	stepList.addStep(function(cb) {
		server.inject({method: 'post', url: '/api/set/' + setName + '/poolentry', payload: poolEntry1Data}, function(result) { cb(); });
	});
	stepList.addStep(function(cb) {
		server.inject({method: 'post', url: '/api/set/' + setName + '/poolentry', payload: poolEntry2Data}, function(result) { cb(); });
	});
	stepList.addStep(function(cb) {
		server.inject({method: 'post', url: '/api/set/' + setName + '/song', payload: songData}, function(res) {
			songId = res.result.id;
			cb();
		});
	});
	stepList.addStep(function(cb) {
		server.inject({method: 'post', url: '/api/set/' + setName + '/song/' + songId + '/songrow', payload: songRow1Data}, function(result) { cb(); });
	});
	stepList.addStep(function(cb) {
		server.inject({method: 'post', url: '/api/set/' + setName + '/song/' + songId + '/songrow', payload: songRow2Data}, function(result) { cb(); });
	});

	stepList.execute(beGone);
}
<commit_msg>Fix create-example-set script creating patternrows.<commit_after>var StepList = require('../../step-list');

var startServerInstance = require('../../server-instance');
var server = startServerInstance(beginCommand);

function beginCommand() {

	function beGone() {
		server.stop(function() {
			console.log('done.');
			process.exit();
		});
	}

	var setName = 'example';
	var setData = {
		name: setName,
		swing: 0.5,
		bpm: 130
	};
	var poolEntry1Data = {
		name: 'pool-entry-1',
		volume: 1,
		sampleType: 'local',
		sampleId: '1234-5678'
	};
	var poolEntry2Data = {
		name: 'pool-entry-2',
		volume: 0.8,
		sampleType: 'local',
		sampleId: '0123-4567'
	};
	var pattern1Data = {
		name: 'pattern-1',
		length: 16,
		locked: false
	};
	var pattern2Data = {
		name: 'pattern-2',
		length: 16,
		locked: false
	};
	var patternRow1Data = {
		poolEntry: 'pool-entry-1',
		steps: [1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0]
	};
	var patternRow2Data = {
		poolEntry: 'pool-entry-2',
		steps: [0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0]
	};
	var songData = {
		name: 'song-1',
		locked: false
	};
	var songRow1Data = {
		pattern_id: 'pattern-1',
		offset: 0,
		len: 16,
		count: 2
	};
	var songRow2Data = {
		pattern_id: 'pattern-2',
		offset: 0,
		len: 16,
		count: 1
	};

	var stepList = new StepList();

	var pattern1Id, pattern2Id, songId;

	stepList.addStep(function(cb) {
		server.inject({method: 'get', url: '/api/set/' + setName}, function(result) { cb(); });
	});
	stepList.addStep(function(cb) {
		server.inject({method: 'post', url: '/api/set/' + setName + '/pattern', payload: pattern1Data}, function(res) {
			pattern1Id = res.result.id;
			cb();
		});
	});
	stepList.addStep(function(cb) {
		server.inject({method: 'post', url: '/api/set/' + setName + '/pattern', payload: pattern2Data}, function(res) {
			pattern2Id = res.result.id;
			cb();
		});
	});
	stepList.addStep(function(cb) {
		server.inject({method: 'post', url: '/api/set/' + setName + '/pattern/' + pattern1Id + '/patternrow', payload: patternRow1Data}, function(result) { cb(); });
	});
	stepList.addStep(function(cb) {
		server.inject({method: 'post', url: '/api/set/' + setName + '/pattern/' + pattern1Id + '/patternrow', payload: patternRow2Data}, function(result) { cb(); });
	});
	stepList.addStep(function(cb) {
		server.inject({method: 'post', url: '/api/set/' + setName + '/pattern/' + pattern2Id + '/patternrow', payload: patternRow1Data}, function(result) { cb(); });
	});
	stepList.addStep(function(cb) {
		server.inject({method: 'post', url: '/api/set/' + setName + '/pattern/' + pattern2Id + '/patternrow', payload: patternRow2Data}, function(result) { cb(); });
	});
	stepList.addStep(function(cb) {
		server.inject({method: 'post', url: '/api/set/' + setName + '/poolentry', payload: poolEntry1Data}, function(result) { cb(); });
	});
	stepList.addStep(function(cb) {
		server.inject({method: 'post', url: '/api/set/' + setName + '/poolentry', payload: poolEntry2Data}, function(result) { cb(); });
	});
	stepList.addStep(function(cb) {
		server.inject({method: 'post', url: '/api/set/' + setName + '/song', payload: songData}, function(res) {
			songId = res.result.id;
			cb();
		});
	});
	stepList.addStep(function(cb) {
		server.inject({method: 'post', url: '/api/set/' + setName + '/song/' + songId + '/songrow', payload: songRow1Data}, function(result) { cb(); });
	});
	stepList.addStep(function(cb) {
		server.inject({method: 'post', url: '/api/set/' + setName + '/song/' + songId + '/songrow', payload: songRow2Data}, function(result) { cb(); });
	});

	stepList.execute(beGone);
}
<|endoftext|>


<commit_before>
var mouseX = -1;
var mouseY = -1;
var mouseIsDown = false;
var mouseClicked = false;
var mouseCameUp = false;
var mouseCameDown = false;

function Interaction() {

    this.order = 100000;

    mouseX = -1;
    mouseY = -1;
    mouseIsDown = false;
    mouseClicked = false;
    mouseCameUp = false;
    mouseCameDown = false;

    this.doActions = () => {
        //other AI objects get one frame to handle these events and then we clear them
        mouseCameUp = false;
        mouseCameDown = false;
        mouseClicked = false;
    };

    this.handleMouseMove = (event) => {
        mouseX = event.offsetX / scaleFactor;
        mouseY = event.offsetY / scaleFactor;
    }

    this.handleMouseUp = (event) => {
        mouseIsDown = false;
        mouseCameUp = true;
    }

    this.handleMouseDown = () => {
        mouseX = event.offsetX / scaleFactor;
        mouseY = event.offsetY / scaleFactor;
        mouseCameDown = true;
        mouseIsDown = true;
    }

    this.handleClick = () => {
        mouseClicked = true;
    }

    this.handleTouchStart = () => {
        mouseX = event.touches[0].clientX / scaleFactor;
        mouseY = event.touches[0].clientY / scaleFactor;
        mouseCameDown = true;
        mouseIsDown = true;
    }

    this.handleTouchMove = () => {
        mouseX = event.touches[0].clientX / scaleFactor;
        mouseY = event.touches[0].clientY / scaleFactor;
    }

    this.handleTouchEnd = () => {
        mouseX = -1;
        mouseY = -1;
        mouseCameUp = true;
        mouseIsDown = false;
    }

    canvas.addEventListener("mousemove", this.handleMouseMove);
    canvas.addEventListener("mousedown", this.handleMouseDown);
    canvas.addEventListener("mouseup", this.handleMouseUp);
    canvas.addEventListener("click", this.handleClick);

    canvas.addEventListener("touchstart", this.handleTouchStart);
    canvas.addEventListener("touchmove", this.handleTouchMove);
    canvas.addEventListener("touchend", this.handleTouchEnd);

    ai.addObject(this);
}<commit_msg>Fixing a bug that only appears in FireFox.<commit_after>
var mouseX = -1;
var mouseY = -1;
var mouseIsDown = false;
var mouseClicked = false;
var mouseCameUp = false;
var mouseCameDown = false;

function Interaction() {

    this.order = 100000;

    mouseX = -1;
    mouseY = -1;
    mouseIsDown = false;
    mouseClicked = false;
    mouseCameUp = false;
    mouseCameDown = false;

    this.doActions = () => {
        //other AI objects get one frame to handle these events and then we clear them
        mouseCameUp = false;
        mouseCameDown = false;
        mouseClicked = false;
    };

    this.handleMouseMove = (event) => {
        mouseX = event.offsetX / scaleFactor;
        mouseY = event.offsetY / scaleFactor;
    }

    this.handleMouseUp = (event) => {
        mouseIsDown = false;
        mouseCameUp = true;
    }

    this.handleMouseDown = (event) => {
        mouseX = event.offsetX / scaleFactor;
        mouseY = event.offsetY / scaleFactor;
        mouseCameDown = true;
        mouseIsDown = true;
    }

    this.handleClick = () => {
        mouseClicked = true;
    }

    this.handleTouchStart = () => {
        mouseX = event.touches[0].clientX / scaleFactor;
        mouseY = event.touches[0].clientY / scaleFactor;
        mouseCameDown = true;
        mouseIsDown = true;
    }

    this.handleTouchMove = () => {
        mouseX = event.touches[0].clientX / scaleFactor;
        mouseY = event.touches[0].clientY / scaleFactor;
    }

    this.handleTouchEnd = () => {
        mouseX = -1;
        mouseY = -1;
        mouseCameUp = true;
        mouseIsDown = false;
    }

    canvas.addEventListener("mousemove", this.handleMouseMove);
    canvas.addEventListener("mousedown", this.handleMouseDown);
    canvas.addEventListener("mouseup", this.handleMouseUp);
    canvas.addEventListener("click", this.handleClick);

    canvas.addEventListener("touchstart", this.handleTouchStart);
    canvas.addEventListener("touchmove", this.handleTouchMove);
    canvas.addEventListener("touchend", this.handleTouchEnd);

    ai.addObject(this);
}<|endoftext|>


<commit_before>/**
 * Отправляет ajax-запросы и обрабатывает результаты.
 *
 * Добавляет события:
 * form-submit		— срабатывает перед отправкой данных формы
 */
$.ajaxSetup( {
	async : false,
	cache :	false,
	headers : {
		'X-Requested-With' : 'XMLHttpRequest'
	}
} );

var ajaxer = {};
ajaxer.id = 1;

ajaxer.httpRequest = function( url, params, headers ) {

	var data = {};
	if( typeof params == 'undefined' || !params ) {
		data.type = 'GET';
	} else {
		data.type = 'POST';
		data.data = 'json=' + encodeURIComponent( JSON.stringify( params ) );
	}
	if( typeof headers != 'undefined' ) {
		data.headers = headers;
	}
	return $.ajax( url, data ).responseText;
};

ajaxer.sendForm = function( form, event ) {

	var data = {
		form: $( form ).serializeArray()
	};
	//var data = $( event.target ).serialize();
	$( form ).find( '.required' ).fadeOut( 'fast' );
	$( form ).find( '.invalid' ).fadeOut( 'fast' );
	$( form ).find( '.status' ).fadeOut( 'fast' );
	$( form ).find( '.problem' ).removeClass( 'problem' );
	ajaxer.process( this.httpRequest( $( form ).attr( 'action' ), data ), form );
};

ajaxer.query = function( url, data ) {

	ajaxer.process( this.httpRequest( url, data ) );
};

ajaxer.process = function( data, form ) {

	try {
		console.info( 'Server said:', data );
		var data1 = $.parseJSON( data );
		if( !data1.actions ) {
			throw "data error";
		}
		this.clean( form );
		for( var key in data1.actions ) {
			var action = data1.actions[key];
			switch( action.action ) {
			case 'notify':	// сообщение
				this.notify( action.lang, action.message );
				break;
			case 'require':// не заполнено обязательное поле формы
				this.require( form, action.name );
				break;
			case 'invalid':	// не правильное значение поля формы
				this.invalid( form, action.name, action.message );
				break;
			case 'status': // текстовый статус для поля
				this.status( form, action.name, action.message, action.classname );
				break;
			case 'redirect':// перенаправление
				this.redirect( action.url );
				break;
			case 'display':	// показать окно с пришедшим html
				this.display( action.html );
				break;
			case 'reload': // перезагрузить страницу
				this.reload();
				break;
			case 'close':
				this.close( form );
				break;
			case 'error':	// сообщение об ошибке
				this.error( action.lang, action.message );
				break;
			case 'reset':	// сделать форме reset
				this.reset( form );
				break;
			default:
				console.warn( 'Ajaxer action "' + action.action + '" not implemented' );
			}
		}
	} catch( ex ) {
		this.notify( {close:'OK'}, 'Unknown error.' );
		console.warn( 'Server returned:', data );
	}
};

ajaxer.clean = function( form ) {

	$( form ).find( '.problem' ).removeClass( 'problem' );
};

ajaxer.notify = function( lang, message ) {

	$( 'body' ).append(
		'
' +
			'
' +
			'
' +
			'

' +
			'
' + message + '
' +
			'' + ( lang.close ? lang.close : 'OK' ) + '' +
			'
' +
			'
' +
			'
'
	);
	$( '#ajaxer-' + ajaxer.id ).find( '.overlay-inner' ).fadeIn( 'fast' );
	$( window ).resize();
	ajaxer.id++;
};

ajaxer.error = function( lang, message ) {

	ajaxer.notify( lang, message );
};

ajaxer.require = function( form, name ) {

	var el = $( form ).find( '[name=' + name + ']' );
	if( !el.length || el.attr( 'type' ) == 'hidden' ) {
		ajaxer.error( {}, 'Field "' + name + '" is required.' );
		return;
	}
	var cke = $( form ).find( '#cke_' + name );
	if( cke.length ) {
		cke.addClass( 'problem' );
	} else {
		el.addClass( 'problem' );
	}
	var container = el.closest( '.container' );
	if( !container.length ) {
		return;
	}
	cke = $( form ).find( '#cke_' + name );
	if( cke.length ) {
		cke.addClass( 'problem' );
	} else {
		container.addClass( 'problem' );
	}
	var req = container.find( '.required' );
	if( !req.length ) {
		return;
	}
	req.fadeIn();
};

ajaxer.invalid = function( form, name, message ) {

	var el = $( form ).find( '[name=' + name + ']' );
	if( !el.length || el.attr( 'type' ) == 'hidden' ) {
		ajaxer.error( {}, 'Invalid value for field "' + name + '".' );
		return;
	}
	var cke = $( form ).find( '#cke_' + name );
	if( cke.length ) {
		cke.addClass( 'problem' );
	} else {
		el.addClass( 'problem' );
	}
	var container = el.closest( '.container' );
	if( !container.length ) {
		return;
	}
	cke = $( form ).find( '#cke_' + name );
	if( cke.length ) {
		cke.addClass( 'problem' );
	} else {
		container.addClass( 'problem' );
	}
	var req = container.find( '.invalid' );
	if( !req.length ) {
		return;
	}
	if( message ) {
		req.find( '.invalid-text' ).html( message );
	}
	req.fadeIn( 'fast' );
};

ajaxer.status = function( form, name, message, classname ) {

	var status = $( form ).find( '[name=' + name + ']' ).parents( '.container' ).find( '.status' );
	if( status ) {
		status.fadeIn( 'fast' );
		status.attr( 'class', 'status ' + classname );
		status.html( message );
	}
};

ajaxer.redirect = function( url ) {

	if( typeof(switcher) != 'undefined' ) {
		switcher.page( url );
	} else {
		document.location( url );
	}
};

ajaxer.reload = function() {

	window.location.reload();
};

ajaxer.display = function( html ) {

	$( 'body' ).append(
		'
' +
			'
' +
			'
' +
			'

' +
			html +
			'
' +
			'
' +
			'
'
	);
	$( '#ajaxer-' + ajaxer.id ).find( '.overlay-inner' ).fadeIn( 'fast' );
	$( window ).resize();
	ajaxer.id++;
};

ajaxer.close = function( obj ) {

	var el = $( obj ).parents( '.overlay' );
	el.fadeOut( 'fast', function() {
		$( this ).remove();
	} );
};

ajaxer.reset = function( form ) {

	$( form ).get( 0 ).reset();
};

var main = {};
main.httpRequest = ajaxer.httpRequest;

$( document ).delegate( 'form.ajaxer', 'submit', function( event ) {

	var form = $( this );
	var files = form.find( 'input[type=file]' );
	$( document ).triggerHandler( 'form-submit' );
	if( !files.length ) {
		// serialize method
		event.preventDefault();
		ajaxer.sendForm( this, event );
	} else {
		// iframe method
		if( !$( '#ajaxerFrame' ).length ) {
			var frame = $( '' );
			var uuid = '';
			for( var i = 0; i < 32; i++ ) {
				uuid += Math.floor( Math.random() * 16 ).toString( 16 );
			}
			var originalAction = form.attr( 'action' );
			form.attr( 'method', 'post' );
			form.attr( 'enctype', 'multipart/form-data' );
			form.attr( 'action', form.attr( 'action' ) + ( originalAction.indexOf( '?' ) == -1 ? '?' : '&' ) + 'X-Progress-ID=' + uuid );
			$( 'body' ).append( frame );
			form.attr( 'target', 'ajaxerFrame' );
			form.append( '<input type="hidden" name="_method" value="iframe"/>' );
			var loading = $( '#loading' );
			if( !loading.length ) {
				$( 'body' ).append( loading = $( '

' ) );
			}
			loading.fadeIn();
			var interval = window.setInterval(
				function() {
					ajaxer.fetchProgress( uuid );
				},
				1000
			);
			frame.load( function() {
				var rawframe = frame.get( 0 );
				if( rawframe.contentDocument ) {
					var val = rawframe.contentDocument.body.innerHTML;
				} else if( rawframe.contentWindow ) {
					val = rawframe.contentWindow.document.body.innerHTML;
				} else if( rawframe.document ) {
					val = rawframe.document.body.innerHTML;
				}
				try {
					var fc = $( val ).text();
					if( fc ) {
						val = fc;
					}
				} catch( e ) {}
				form.attr( 'action', originalAction );
				form.find( 'input[name=_method]' ).remove();
				$( 'iframe#ajaxerFrame' ).remove();
				loading.find( 'td1' ).css( 'width', Math.ceil( $( '#upprog' ).width() - 20 ) + 'px' );
				loading.fadeOut( 'slow', function() {
					window.clearTimeout( interval );
					loading.find( '#upprog' ).remove();
				} );
				ajaxer.process( val, form );
			} );
		}
	}
} );

ajaxer.fetchProgress = function( uuid ) {

	var res = ajaxer.httpRequest( '/progress', null, { 'X-Progress-ID': uuid } );
	res = $.parseJSON( res );
	if( res.state == 'uploading' ) {
		var progressbar = $( '#upprog' );
		if( !progressbar.length ) {
			$( '#loading' )
				.css( 'background-image', 'none' )
				.append( '

' );
			autocenter();
			progressbar = $( '#upprog' );
		}
		progressbar.find( '.td1' )
			.css( 'width', Math.ceil( ( progressbar.width() - 20 ) * res.received / res.size ) + 'px' );
	}
};

$( 'a.ajaxer' ).live( 'click dblclick', function( e ) {
	var href = $( this ).attr( 'href' );
	if( href && href != '#' ) {
		ajaxer.query( href );
	}
	e.preventDefault();
} );

$( '.ajaxer input' ).live( 'keypress', function( e ) {
	if( e.which == 13 ) {
		$( this ).parents( 'form' ).submit();
		e.preventDefault();
	}
} );

$( '.submit' ).live( 'click dblclick', function( e ) {
	$( this ).parents( 'form' ).submit();
	e.preventDefault();
});

$( '.reset' ).live( 'click dblclick', function( e ) {
	ajaxer.reset( $( this ).parents( 'form' ) );
	e.preventDefault();
} );

ajaxer.watcher = function() {

	var mc = $.cookie( 'query' );
	if( mc ) {
		mc = $.parseJSON( mc );
		ajaxer.query( mc.url );
		$.cookie( 'query', null, { path: "/", domain: config.mainhost ? '.' + config.mainhost : false } );
	}
	mc = $.cookie( 'notify' );
	if( mc ) {
		mc = $.parseJSON( mc );
		if( mc.type == 'error' ) {
			ajaxer.error( mc.lang, mc.message );
		} else {
			ajaxer.notify( mc.lang, mc.message );
		}
		if( config.mainhost ) {
			$.cookie( 'notify', null, { path:"/", domain:config.mainhost } );
		} else {
			$.cookie( 'notify', null, { path:"/", domain:window.location.host } );
		}
	}
};

$( document ).ready( ajaxer.watcher );
$( document ).bind( 'construct', ajaxer.watcher );<commit_msg>И ещё попытка.<commit_after>/**
 * Отправляет ajax-запросы и обрабатывает результаты.
 *
 * Добавляет события:
 * form-submit		— срабатывает перед отправкой данных формы
 */
$.ajaxSetup( {
	async : false,
	cache :	false,
	headers : {
		'X-Requested-With' : 'XMLHttpRequest'
	}
} );

var ajaxer = {};
ajaxer.id = 1;

ajaxer.httpRequest = function( url, params, headers ) {

	var data = {};
	if( typeof params == 'undefined' || !params ) {
		data.type = 'GET';
	} else {
		data.type = 'POST';
		data.data = 'json=' + encodeURIComponent( JSON.stringify( params ) );
	}
	if( typeof headers != 'undefined' ) {
		data.headers = headers;
	}
	return $.ajax( url, data ).responseText;
};

ajaxer.sendForm = function( form, event ) {

	var data = {
		form: $( form ).serializeArray()
	};
	//var data = $( event.target ).serialize();
	$( form ).find( '.required' ).fadeOut( 'fast' );
	$( form ).find( '.invalid' ).fadeOut( 'fast' );
	$( form ).find( '.status' ).fadeOut( 'fast' );
	$( form ).find( '.problem' ).removeClass( 'problem' );
	ajaxer.process( this.httpRequest( $( form ).attr( 'action' ), data ), form );
};

ajaxer.query = function( url, data ) {

	ajaxer.process( this.httpRequest( url, data ) );
};

ajaxer.process = function( data, form ) {

	try {
		console.info( 'Server said:', data );
		var data1 = $.parseJSON( data );
		if( !data1.actions ) {
			throw "data error";
		}
		this.clean( form );
		for( var key in data1.actions ) {
			var action = data1.actions[key];
			switch( action.action ) {
			case 'notify':	// сообщение
				this.notify( action.lang, action.message );
				break;
			case 'require':// не заполнено обязательное поле формы
				this.require( form, action.name );
				break;
			case 'invalid':	// не правильное значение поля формы
				this.invalid( form, action.name, action.message );
				break;
			case 'status': // текстовый статус для поля
				this.status( form, action.name, action.message, action.classname );
				break;
			case 'redirect':// перенаправление
				this.redirect( action.url );
				break;
			case 'display':	// показать окно с пришедшим html
				this.display( action.html );
				break;
			case 'reload': // перезагрузить страницу
				this.reload();
				break;
			case 'close':
				this.close( form );
				break;
			case 'error':	// сообщение об ошибке
				this.error( action.lang, action.message );
				break;
			case 'reset':	// сделать форме reset
				this.reset( form );
				break;
			default:
				console.warn( 'Ajaxer action "' + action.action + '" not implemented' );
			}
		}
	} catch( ex ) {
		this.notify( {close:'OK'}, 'Unknown error.' );
		console.warn( 'Server returned:', data );
	}
};

ajaxer.clean = function( form ) {

	$( form ).find( '.problem' ).removeClass( 'problem' );
};

ajaxer.notify = function( lang, message ) {

	$( 'body' ).append(
		'
' +
			'
' +
			'
' +
			'

' +
			'
' + message + '
' +
			'' + ( lang.close ? lang.close : 'OK' ) + '' +
			'
' +
			'
' +
			'
'
	);
	$( '#ajaxer-' + ajaxer.id ).find( '.overlay-inner' ).fadeIn( 'fast' );
	$( window ).resize();
	ajaxer.id++;
};

ajaxer.error = function( lang, message ) {

	ajaxer.notify( lang, message );
};

ajaxer.require = function( form, name ) {

	var el = $( form ).find( '[name=' + name + ']' );
	if( !el.length || el.attr( 'type' ) == 'hidden' ) {
		ajaxer.error( {}, 'Field "' + name + '" is required.' );
		return;
	}
	var cke = $( form ).find( '#cke_' + name );
	if( cke.length ) {
		cke.addClass( 'problem' );
	} else {
		el.addClass( 'problem' );
	}
	var container = el.closest( '.container' );
	if( !container.length ) {
		return;
	}
	cke = $( form ).find( '#cke_' + name );
	if( cke.length ) {
		cke.addClass( 'problem' );
	} else {
		container.addClass( 'problem' );
	}
	var req = container.find( '.required' );
	if( !req.length ) {
		return;
	}
	req.fadeIn();
};

ajaxer.invalid = function( form, name, message ) {

	var el = $( form ).find( '[name=' + name + ']' );
	if( !el.length || el.attr( 'type' ) == 'hidden' ) {
		ajaxer.error( {}, 'Invalid value for field "' + name + '".' );
		return;
	}
	var cke = $( form ).find( '#cke_' + name );
	if( cke.length ) {
		cke.addClass( 'problem' );
	} else {
		el.addClass( 'problem' );
	}
	var container = el.closest( '.container' );
	if( !container.length ) {
		return;
	}
	cke = $( form ).find( '#cke_' + name );
	if( cke.length ) {
		cke.addClass( 'problem' );
	} else {
		container.addClass( 'problem' );
	}
	var req = container.find( '.invalid' );
	if( !req.length ) {
		return;
	}
	if( message ) {
		req.find( '.invalid-text' ).html( message );
	}
	req.fadeIn( 'fast' );
};

ajaxer.status = function( form, name, message, classname ) {

	var status = $( form ).find( '[name=' + name + ']' ).parents( '.container' ).find( '.status' );
	if( status ) {
		status.fadeIn( 'fast' );
		status.attr( 'class', 'status ' + classname );
		status.html( message );
	}
};

ajaxer.redirect = function( url ) {

	if( typeof(switcher) != 'undefined' ) {
		switcher.page( url );
	} else {
		document.location( url );
	}
};

ajaxer.reload = function() {

	window.location.reload();
};

ajaxer.display = function( html ) {

	$( 'body' ).append(
		'
' +
			'
' +
			'
' +
			'

' +
			html +
			'
' +
			'
' +
			'
'
	);
	$( '#ajaxer-' + ajaxer.id ).find( '.overlay-inner' ).fadeIn( 'fast' );
	$( window ).resize();
	ajaxer.id++;
};

ajaxer.close = function( obj ) {

	var el = $( obj ).parents( '.overlay' );
	el.fadeOut( 'fast', function() {
		$( this ).remove();
	} );
};

ajaxer.reset = function( form ) {

	$( form ).get( 0 ).reset();
};

var main = {};
main.httpRequest = ajaxer.httpRequest;

$( document ).delegate( 'form.ajaxer', 'submit', function( event ) {

	var form = $( this );
	var files = form.find( 'input[type=file]' );
	$( document ).triggerHandler( 'form-submit' );
	if( !files.length ) {
		// serialize method
		event.preventDefault();
		ajaxer.sendForm( this, event );
	} else {
		// iframe method
		if( !$( '#ajaxerFrame' ).length ) {
			var frame = $( '' );
			var uuid = '';
			for( var i = 0; i < 32; i++ ) {
				uuid += Math.floor( Math.random() * 16 ).toString( 16 );
			}
			var originalAction = form.attr( 'action' );
			form.attr( 'method', 'post' );
			form.attr( 'enctype', 'multipart/form-data' );
			form.attr( 'action', form.attr( 'action' ) + ( originalAction.indexOf( '?' ) == -1 ? '?' : '&' ) + 'X-Progress-ID=' + uuid );
			$( 'body' ).append( frame );
			form.attr( 'target', 'ajaxerFrame' );
			form.append( '<input type="hidden" name="_method" value="iframe"/>' );
			var loading = $( '#loading' );
			if( !loading.length ) {
				$( 'body' ).append( loading = $( '

' ) );
			}
			loading.fadeIn();
			var interval = window.setInterval(
				function() {
					ajaxer.fetchProgress( uuid );
				},
				1000
			);
			frame.load( function() {
				var rawframe = frame.get( 0 );
				if( rawframe.contentDocument ) {
					var val = rawframe.contentDocument.body.innerHTML;
				} else if( rawframe.contentWindow ) {
					val = rawframe.contentWindow.document.body.innerHTML;
				} else if( rawframe.document ) {
					val = rawframe.document.body.innerHTML;
				}
				try {
					var fc = $( val ).text();
					if( fc ) {
						val = fc;
					}
				} catch( e ) {}
				form.attr( 'action', originalAction );
				form.find( 'input[name=_method]' ).remove();
				$( 'iframe#ajaxerFrame' ).remove();
				loading.find( 'td1' ).css( 'width', Math.ceil( $( '#upprog' ).width() - 20 ) + 'px' );
				loading.fadeOut( 'slow', function() {
					window.clearTimeout( interval );
					loading.find( '#upprog' ).remove();
				} );
				ajaxer.process( val, form );
			} );
		}
	}
} );

ajaxer.fetchProgress = function( uuid ) {

	var res = ajaxer.httpRequest( '/progress', null, { 'X-Progress-ID': uuid } );
	res = $.parseJSON( res );
	if( res.state == 'uploading' ) {
		var progressbar = $( '#upprog' );
		if( !progressbar.length ) {
			$( '#loading' )
				.css( 'background-image', 'none' )
				.append( '

' );
			autocenter();
			progressbar = $( '#upprog' );
		}
		progressbar.find( '.td1' )
			.css( 'width', Math.ceil( ( progressbar.width() - 20 ) * res.received / res.size ) + 'px' );
	}
};

$( 'a.ajaxer' ).live( 'click dblclick', function( e ) {
	var href = $( this ).attr( 'href' );
	if( href && href != '#' ) {
		ajaxer.query( href );
	}
	e.preventDefault();
} );

$( '.ajaxer input' ).live( 'keypress', function( e ) {
	if( e.which == 13 ) {
		$( this ).parents( 'form' ).submit();
		e.preventDefault();
	}
} );

$( '.submit' ).live( 'click dblclick', function( e ) {
	$( this ).parents( 'form' ).submit();
	e.preventDefault();
});

$( '.reset' ).live( 'click dblclick', function( e ) {
	ajaxer.reset( $( this ).parents( 'form' ) );
	e.preventDefault();
} );

ajaxer.watcher = function() {

	var mc = $.cookie( 'query' );
	if( mc ) {
		mc = $.parseJSON( mc );
		ajaxer.query( mc.url );
		$.cookie( 'query', null, { path: "/", domain: config.mainhost ? '.' + config.mainhost : false } );
	}
	mc = $.cookie( 'notify' );
	if( mc ) {
		mc = $.parseJSON( mc );
		if( mc.type == 'error' ) {
			ajaxer.error( mc.lang, mc.message );
		} else {
			ajaxer.notify( mc.lang, mc.message );
		}
		if( config.mainhost ) {
			$.cookie( 'notify', null, { path:"/", domain:'.' + config.mainhost } );
		} else {
			$.cookie( 'notify', null, { path:"/", domain:'.' + window.location.host } );
		}
	}
};

$( document ).ready( ajaxer.watcher );
$( document ).bind( 'construct', ajaxer.watcher );<|endoftext|>


<commit_before>const {BrowserWindow, app, dialog, ipcMain} = require('electron')
const path = require('path')
const fs = require('fs')
const url = require('url')
const {EventEmitter} = require('events')

const ICON_PATH = path.resolve(__dirname, '..', '..', 'resources', 'atom.png')

let includeShellLoadTime = true
let nextId = 0

module.exports =
class AtomWindow extends EventEmitter {
  constructor (atomApplication, fileRecoveryService, settings = {}) {
    super()

    this.id = nextId++
    this.atomApplication = atomApplication
    this.fileRecoveryService = fileRecoveryService
    this.isSpec = settings.isSpec
    this.headless = settings.headless
    this.safeMode = settings.safeMode
    this.devMode = settings.devMode
    this.resourcePath = settings.resourcePath

    let {pathToOpen, locationsToOpen} = settings
    if (!locationsToOpen && pathToOpen) locationsToOpen = [{pathToOpen}]
    if (!locationsToOpen) locationsToOpen = []

    this.loadedPromise = new Promise(resolve => { this.resolveLoadedPromise = resolve })
    this.closedPromise = new Promise(resolve => { this.resolveClosedPromise = resolve })

    const options = {
      show: false,
      title: 'Atom',
      tabbingIdentifier: 'atom',
      webPreferences: {
        // Prevent specs from throttling when the window is in the background:
        // this should result in faster CI builds, and an improvement in the
        // local development experience when running specs through the UI (which
        // now won't pause when e.g. minimizing the window).
        backgroundThrottling: !this.isSpec,
        // Disable the `auxclick` feature so that `click` events are triggered in
        // response to a middle-click.
        // (Ref: https://github.com/atom/atom/pull/12696#issuecomment-290496960)
        disableBlinkFeatures: 'Auxclick'
      }
    }

    // Don't set icon on Windows so the exe's ico will be used as window and
    // taskbar's icon. See https://github.com/atom/atom/issues/4811 for more.
    if (process.platform === 'linux') options.icon = ICON_PATH
    if (this.shouldAddCustomTitleBar()) options.titleBarStyle = 'hidden'
    if (this.shouldAddCustomInsetTitleBar()) options.titleBarStyle = 'hidden-inset'
    if (this.shouldHideTitleBar()) options.frame = false
    this.browserWindow = new BrowserWindow(options)

    Object.defineProperty(this.browserWindow, 'loadSettingsJSON', {
      get: () => JSON.stringify(Object.assign({
        userSettings: this.atomApplication.configFile.get()
      }, this.loadSettings)),
      configurable: true
    })

    this.handleEvents()

    this.loadSettings = Object.assign({}, settings)
    this.loadSettings.appVersion = app.getVersion()
    this.loadSettings.resourcePath = this.resourcePath
    this.loadSettings.atomHome = process.env.ATOM_HOME
    if (this.loadSettings.devMode == null) this.loadSettings.devMode = false
    if (this.loadSettings.safeMode == null) this.loadSettings.safeMode = false
    if (this.loadSettings.clearWindowState == null) this.loadSettings.clearWindowState = false

    if (!this.loadSettings.initialPaths) {
      this.loadSettings.initialPaths = []
      for (const {pathToOpen, stat} of locationsToOpen) {
        if (!pathToOpen) continue
        if (stat && stat.isDirectory()) {
          this.loadSettings.initialPaths.push(pathToOpen)
        } else {
          const parentDirectory = path.dirname(pathToOpen)
          if (stat && stat.isFile() || fs.existsSync(parentDirectory)) {
            this.loadSettings.initialPaths.push(parentDirectory)
          } else {
            this.loadSettings.initialPaths.push(pathToOpen)
          }
        }
      }
    }

    this.loadSettings.initialPaths.sort()

    // Only send to the first non-spec window created
    if (includeShellLoadTime && !this.isSpec) {
      includeShellLoadTime = false
      if (!this.loadSettings.shellLoadTime) {
        this.loadSettings.shellLoadTime = Date.now() - global.shellStartTime
      }
    }

    this.representedDirectoryPaths = this.loadSettings.initialPaths
    if (!this.loadSettings.env) this.env = this.loadSettings.env

    this.browserWindow.on('window:loaded', () => {
      this.disableZoom()
      this.emit('window:loaded')
      this.resolveLoadedPromise()
    })

    this.browserWindow.on('window:locations-opened', () => {
      this.emit('window:locations-opened')
    })

    this.browserWindow.on('enter-full-screen', () => {
      this.browserWindow.webContents.send('did-enter-full-screen')
    })

    this.browserWindow.on('leave-full-screen', () => {
      this.browserWindow.webContents.send('did-leave-full-screen')
    })

    this.browserWindow.loadURL(
      url.format({
        protocol: 'file',
        pathname: `${this.resourcePath}/static/index.html`,
        slashes: true
      })
    )

    this.browserWindow.showSaveDialog = this.showSaveDialog.bind(this)

    if (this.isSpec) this.browserWindow.focusOnWebView()

    const hasPathToOpen = !(locationsToOpen.length === 1 && locationsToOpen[0].pathToOpen == null)
    if (hasPathToOpen && !this.isSpecWindow()) this.openLocations(locationsToOpen)
  }

  hasProjectPath () {
    return this.representedDirectoryPaths.length > 0
  }

  setupContextMenu () {
    const ContextMenu = require('./context-menu')

    this.browserWindow.on('context-menu', menuTemplate => {
      return new ContextMenu(menuTemplate, this)
    })
  }

  containsPaths (paths) {
    return paths.every(p => this.containsPath(p))
  }

  containsPath (pathToCheck) {
    if (!pathToCheck) return false
    let stat
    return this.representedDirectoryPaths.some(projectPath => {
      if (pathToCheck === projectPath) return true
      if (!pathToCheck.startsWith(path.join(projectPath, path.sep))) return false
      if (stat === undefined) stat = fs.statSyncNoException(pathToCheck)
      return !stat || !stat.isDirectory()
    })
  }

  handleEvents () {
    this.browserWindow.on('close', async event => {
      if (!this.atomApplication.quitting && !this.unloading) {
        event.preventDefault()
        this.unloading = true
        this.atomApplication.saveCurrentWindowOptions(false)
        if (await this.prepareToUnload()) this.close()
      }
    })

    this.browserWindow.on('closed', () => {
      this.fileRecoveryService.didCloseWindow(this)
      this.atomApplication.removeWindow(this)
      this.resolveClosedPromise()
    })

    this.browserWindow.on('unresponsive', () => {
      if (this.isSpec) return
      dialog.showMessageBox(this.browserWindow, {
        type: 'warning',
        buttons: ['Force Close', 'Keep Waiting'],
        message: 'Editor is not responding',
        detail:
          'The editor is not responding. Would you like to force close it or just keep waiting?'
      }, response => { if (response === 0) this.browserWindow.destroy() })
    })

    this.browserWindow.webContents.on('crashed', async () => {
      if (this.headless) {
        console.log('Renderer process crashed, exiting')
        this.atomApplication.exit(100)
        return
      }

      await this.fileRecoveryService.didCrashWindow(this)
      dialog.showMessageBox(this.browserWindow, {
        type: 'warning',
        buttons: ['Close Window', 'Reload', 'Keep It Open'],
        message: 'The editor has crashed',
        detail: 'Please report this issue to https://github.com/atom/atom'
      }, response => {
        switch (response) {
          case 0: return this.browserWindow.destroy()
          case 1: return this.browserWindow.reload()
        }
      })
    })

    this.browserWindow.webContents.on('will-navigate', (event, url) => {
      if (url !== this.browserWindow.webContents.getURL()) event.preventDefault()
    })

    this.setupContextMenu()

    // Spec window's web view should always have focus
    if (this.isSpec) this.browserWindow.on('blur', () => this.browserWindow.focusOnWebView())
  }

  async prepareToUnload () {
    if (this.isSpecWindow()) return true

    this.lastPrepareToUnloadPromise = new Promise(resolve => {
      const callback = (event, result) => {
        if (BrowserWindow.fromWebContents(event.sender) === this.browserWindow) {
          ipcMain.removeListener('did-prepare-to-unload', callback)
          if (!result) {
            this.unloading = false
            this.atomApplication.quitting = false
          }
          resolve(result)
        }
      }
      ipcMain.on('did-prepare-to-unload', callback)
      this.browserWindow.webContents.send('prepare-to-unload')
    })

    return this.lastPrepareToUnloadPromise
  }

  openPath (pathToOpen, initialLine, initialColumn) {
    return this.openLocations([{pathToOpen, initialLine, initialColumn}])
  }

  async openLocations (locationsToOpen) {
    await this.loadedPromise
    this.sendMessage('open-locations', locationsToOpen)
  }

  didChangeUserSettings (settings) {
    this.sendMessage('did-change-user-settings', settings)
  }

  didFailToReadUserSettings (message) {
    this.sendMessage('did-fail-to-read-user-settings', message)
  }

  replaceEnvironment (env) {
    this.browserWindow.webContents.send('environment', env)
  }

  sendMessage (message, detail) {
    this.browserWindow.webContents.send('message', message, detail)
  }

  sendCommand (command, ...args) {
    if (this.isSpecWindow()) {
      if (!this.atomApplication.sendCommandToFirstResponder(command)) {
        switch (command) {
          case 'window:reload': return this.reload()
          case 'window:toggle-dev-tools': return this.toggleDevTools()
          case 'window:close': return this.close()
        }
      }
    } else if (this.isWebViewFocused()) {
      this.sendCommandToBrowserWindow(command, ...args)
    } else if (!this.atomApplication.sendCommandToFirstResponder(command)) {
      this.sendCommandToBrowserWindow(command, ...args)
    }
  }

  sendURIMessage (uri) {
    this.browserWindow.webContents.send('uri-message', uri)
  }

  sendCommandToBrowserWindow (command, ...args) {
    const action = args[0] && args[0].contextCommand
      ? 'context-command'
      : 'command'
    this.browserWindow.webContents.send(action, command, ...args)
  }

  getDimensions () {
    const [x, y] = Array.from(this.browserWindow.getPosition())
    const [width, height] = Array.from(this.browserWindow.getSize())
    return {x, y, width, height}
  }

  shouldAddCustomTitleBar () {
    return (
      !this.isSpec &&
      process.platform === 'darwin' &&
      this.atomApplication.config.get('core.titleBar') === 'custom'
    )
  }

  shouldAddCustomInsetTitleBar () {
    return (
      !this.isSpec &&
      process.platform === 'darwin' &&
      this.atomApplication.config.get('core.titleBar') === 'custom-inset'
    )
  }

  shouldHideTitleBar () {
    return (
      !this.isSpec &&
      process.platform === 'darwin' &&
      this.atomApplication.config.get('core.titleBar') === 'hidden'
    )
  }

  close () {
    return this.browserWindow.close()
  }

  focus () {
    return this.browserWindow.focus()
  }

  minimize () {
    return this.browserWindow.minimize()
  }

  maximize () {
    return this.browserWindow.maximize()
  }

  unmaximize () {
    return this.browserWindow.unmaximize()
  }

  restore () {
    return this.browserWindow.restore()
  }

  setFullScreen (fullScreen) {
    return this.browserWindow.setFullScreen(fullScreen)
  }

  setAutoHideMenuBar (autoHideMenuBar) {
    return this.browserWindow.setAutoHideMenuBar(autoHideMenuBar)
  }

  handlesAtomCommands () {
    return !this.isSpecWindow() && this.isWebViewFocused()
  }

  isFocused () {
    return this.browserWindow.isFocused()
  }

  isMaximized () {
    return this.browserWindow.isMaximized()
  }

  isMinimized () {
    return this.browserWindow.isMinimized()
  }

  isWebViewFocused () {
    return this.browserWindow.isWebViewFocused()
  }

  isSpecWindow () {
    return this.isSpec
  }

  reload () {
    this.loadedPromise = new Promise(resolve => { this.resolveLoadedPromise = resolve })
    this.prepareToUnload().then(canUnload => {
      if (canUnload) this.browserWindow.reload()
    })
    return this.loadedPromise
  }

  showSaveDialog (options, callback) {
    options = Object.assign({
      title: 'Save File',
      defaultPath: this.representedDirectoryPaths[0]
    }, options)

    if (typeof callback === 'function') {
      // Async
      dialog.showSaveDialog(this.browserWindow, options, callback)
    } else {
      // Sync
      return dialog.showSaveDialog(this.browserWindow, options)
    }
  }

  toggleDevTools () {
    return this.browserWindow.toggleDevTools()
  }

  openDevTools () {
    return this.browserWindow.openDevTools()
  }

  closeDevTools () {
    return this.browserWindow.closeDevTools()
  }

  setDocumentEdited (documentEdited) {
    return this.browserWindow.setDocumentEdited(documentEdited)
  }

  setRepresentedFilename (representedFilename) {
    return this.browserWindow.setRepresentedFilename(representedFilename)
  }

  setRepresentedDirectoryPaths (representedDirectoryPaths) {
    this.representedDirectoryPaths = representedDirectoryPaths
    this.representedDirectoryPaths.sort()
    this.loadSettings.initialPaths = this.representedDirectoryPaths
    return this.atomApplication.saveCurrentWindowOptions()
  }

  didClosePathWithWaitSession (path) {
    this.atomApplication.windowDidClosePathWithWaitSession(this, path)
  }

  copy () {
    return this.browserWindow.copy()
  }

  disableZoom () {
    return this.browserWindow.webContents.setVisualZoomLevelLimits(1, 1)
  }
}
<commit_msg>Fix regression where config file was loaded in specs<commit_after>const {BrowserWindow, app, dialog, ipcMain} = require('electron')
const path = require('path')
const fs = require('fs')
const url = require('url')
const {EventEmitter} = require('events')

const ICON_PATH = path.resolve(__dirname, '..', '..', 'resources', 'atom.png')

let includeShellLoadTime = true
let nextId = 0

module.exports =
class AtomWindow extends EventEmitter {
  constructor (atomApplication, fileRecoveryService, settings = {}) {
    super()

    this.id = nextId++
    this.atomApplication = atomApplication
    this.fileRecoveryService = fileRecoveryService
    this.isSpec = settings.isSpec
    this.headless = settings.headless
    this.safeMode = settings.safeMode
    this.devMode = settings.devMode
    this.resourcePath = settings.resourcePath

    let {pathToOpen, locationsToOpen} = settings
    if (!locationsToOpen && pathToOpen) locationsToOpen = [{pathToOpen}]
    if (!locationsToOpen) locationsToOpen = []

    this.loadedPromise = new Promise(resolve => { this.resolveLoadedPromise = resolve })
    this.closedPromise = new Promise(resolve => { this.resolveClosedPromise = resolve })

    const options = {
      show: false,
      title: 'Atom',
      tabbingIdentifier: 'atom',
      webPreferences: {
        // Prevent specs from throttling when the window is in the background:
        // this should result in faster CI builds, and an improvement in the
        // local development experience when running specs through the UI (which
        // now won't pause when e.g. minimizing the window).
        backgroundThrottling: !this.isSpec,
        // Disable the `auxclick` feature so that `click` events are triggered in
        // response to a middle-click.
        // (Ref: https://github.com/atom/atom/pull/12696#issuecomment-290496960)
        disableBlinkFeatures: 'Auxclick'
      }
    }

    // Don't set icon on Windows so the exe's ico will be used as window and
    // taskbar's icon. See https://github.com/atom/atom/issues/4811 for more.
    if (process.platform === 'linux') options.icon = ICON_PATH
    if (this.shouldAddCustomTitleBar()) options.titleBarStyle = 'hidden'
    if (this.shouldAddCustomInsetTitleBar()) options.titleBarStyle = 'hidden-inset'
    if (this.shouldHideTitleBar()) options.frame = false
    this.browserWindow = new BrowserWindow(options)

    Object.defineProperty(this.browserWindow, 'loadSettingsJSON', {
      get: () => JSON.stringify(Object.assign({
        userSettings: !this.isSpec
          ? this.atomApplication.configFile.get()
          : null
      }, this.loadSettings)),
      configurable: true
    })

    this.handleEvents()

    this.loadSettings = Object.assign({}, settings)
    this.loadSettings.appVersion = app.getVersion()
    this.loadSettings.resourcePath = this.resourcePath
    this.loadSettings.atomHome = process.env.ATOM_HOME
    if (this.loadSettings.devMode == null) this.loadSettings.devMode = false
    if (this.loadSettings.safeMode == null) this.loadSettings.safeMode = false
    if (this.loadSettings.clearWindowState == null) this.loadSettings.clearWindowState = false

    if (!this.loadSettings.initialPaths) {
      this.loadSettings.initialPaths = []
      for (const {pathToOpen, stat} of locationsToOpen) {
        if (!pathToOpen) continue
        if (stat && stat.isDirectory()) {
          this.loadSettings.initialPaths.push(pathToOpen)
        } else {
          const parentDirectory = path.dirname(pathToOpen)
          if (stat && stat.isFile() || fs.existsSync(parentDirectory)) {
            this.loadSettings.initialPaths.push(parentDirectory)
          } else {
            this.loadSettings.initialPaths.push(pathToOpen)
          }
        }
      }
    }

    this.loadSettings.initialPaths.sort()

    // Only send to the first non-spec window created
    if (includeShellLoadTime && !this.isSpec) {
      includeShellLoadTime = false
      if (!this.loadSettings.shellLoadTime) {
        this.loadSettings.shellLoadTime = Date.now() - global.shellStartTime
      }
    }

    this.representedDirectoryPaths = this.loadSettings.initialPaths
    if (!this.loadSettings.env) this.env = this.loadSettings.env

    this.browserWindow.on('window:loaded', () => {
      this.disableZoom()
      this.emit('window:loaded')
      this.resolveLoadedPromise()
    })

    this.browserWindow.on('window:locations-opened', () => {
      this.emit('window:locations-opened')
    })

    this.browserWindow.on('enter-full-screen', () => {
      this.browserWindow.webContents.send('did-enter-full-screen')
    })

    this.browserWindow.on('leave-full-screen', () => {
      this.browserWindow.webContents.send('did-leave-full-screen')
    })

    this.browserWindow.loadURL(
      url.format({
        protocol: 'file',
        pathname: `${this.resourcePath}/static/index.html`,
        slashes: true
      })
    )

    this.browserWindow.showSaveDialog = this.showSaveDialog.bind(this)

    if (this.isSpec) this.browserWindow.focusOnWebView()

    const hasPathToOpen = !(locationsToOpen.length === 1 && locationsToOpen[0].pathToOpen == null)
    if (hasPathToOpen && !this.isSpecWindow()) this.openLocations(locationsToOpen)
  }

  hasProjectPath () {
    return this.representedDirectoryPaths.length > 0
  }

  setupContextMenu () {
    const ContextMenu = require('./context-menu')

    this.browserWindow.on('context-menu', menuTemplate => {
      return new ContextMenu(menuTemplate, this)
    })
  }

  containsPaths (paths) {
    return paths.every(p => this.containsPath(p))
  }

  containsPath (pathToCheck) {
    if (!pathToCheck) return false
    let stat
    return this.representedDirectoryPaths.some(projectPath => {
      if (pathToCheck === projectPath) return true
      if (!pathToCheck.startsWith(path.join(projectPath, path.sep))) return false
      if (stat === undefined) stat = fs.statSyncNoException(pathToCheck)
      return !stat || !stat.isDirectory()
    })
  }

  handleEvents () {
    this.browserWindow.on('close', async event => {
      if (!this.atomApplication.quitting && !this.unloading) {
        event.preventDefault()
        this.unloading = true
        this.atomApplication.saveCurrentWindowOptions(false)
        if (await this.prepareToUnload()) this.close()
      }
    })

    this.browserWindow.on('closed', () => {
      this.fileRecoveryService.didCloseWindow(this)
      this.atomApplication.removeWindow(this)
      this.resolveClosedPromise()
    })

    this.browserWindow.on('unresponsive', () => {
      if (this.isSpec) return
      dialog.showMessageBox(this.browserWindow, {
        type: 'warning',
        buttons: ['Force Close', 'Keep Waiting'],
        message: 'Editor is not responding',
        detail:
          'The editor is not responding. Would you like to force close it or just keep waiting?'
      }, response => { if (response === 0) this.browserWindow.destroy() })
    })

    this.browserWindow.webContents.on('crashed', async () => {
      if (this.headless) {
        console.log('Renderer process crashed, exiting')
        this.atomApplication.exit(100)
        return
      }

      await this.fileRecoveryService.didCrashWindow(this)
      dialog.showMessageBox(this.browserWindow, {
        type: 'warning',
        buttons: ['Close Window', 'Reload', 'Keep It Open'],
        message: 'The editor has crashed',
        detail: 'Please report this issue to https://github.com/atom/atom'
      }, response => {
        switch (response) {
          case 0: return this.browserWindow.destroy()
          case 1: return this.browserWindow.reload()
        }
      })
    })

    this.browserWindow.webContents.on('will-navigate', (event, url) => {
      if (url !== this.browserWindow.webContents.getURL()) event.preventDefault()
    })

    this.setupContextMenu()

    // Spec window's web view should always have focus
    if (this.isSpec) this.browserWindow.on('blur', () => this.browserWindow.focusOnWebView())
  }

  async prepareToUnload () {
    if (this.isSpecWindow()) return true

    this.lastPrepareToUnloadPromise = new Promise(resolve => {
      const callback = (event, result) => {
        if (BrowserWindow.fromWebContents(event.sender) === this.browserWindow) {
          ipcMain.removeListener('did-prepare-to-unload', callback)
          if (!result) {
            this.unloading = false
            this.atomApplication.quitting = false
          }
          resolve(result)
        }
      }
      ipcMain.on('did-prepare-to-unload', callback)
      this.browserWindow.webContents.send('prepare-to-unload')
    })

    return this.lastPrepareToUnloadPromise
  }

  openPath (pathToOpen, initialLine, initialColumn) {
    return this.openLocations([{pathToOpen, initialLine, initialColumn}])
  }

  async openLocations (locationsToOpen) {
    await this.loadedPromise
    this.sendMessage('open-locations', locationsToOpen)
  }

  didChangeUserSettings (settings) {
    this.sendMessage('did-change-user-settings', settings)
  }

  didFailToReadUserSettings (message) {
    this.sendMessage('did-fail-to-read-user-settings', message)
  }

  replaceEnvironment (env) {
    this.browserWindow.webContents.send('environment', env)
  }

  sendMessage (message, detail) {
    this.browserWindow.webContents.send('message', message, detail)
  }

  sendCommand (command, ...args) {
    if (this.isSpecWindow()) {
      if (!this.atomApplication.sendCommandToFirstResponder(command)) {
        switch (command) {
          case 'window:reload': return this.reload()
          case 'window:toggle-dev-tools': return this.toggleDevTools()
          case 'window:close': return this.close()
        }
      }
    } else if (this.isWebViewFocused()) {
      this.sendCommandToBrowserWindow(command, ...args)
    } else if (!this.atomApplication.sendCommandToFirstResponder(command)) {
      this.sendCommandToBrowserWindow(command, ...args)
    }
  }

  sendURIMessage (uri) {
    this.browserWindow.webContents.send('uri-message', uri)
  }

  sendCommandToBrowserWindow (command, ...args) {
    const action = args[0] && args[0].contextCommand
      ? 'context-command'
      : 'command'
    this.browserWindow.webContents.send(action, command, ...args)
  }

  getDimensions () {
    const [x, y] = Array.from(this.browserWindow.getPosition())
    const [width, height] = Array.from(this.browserWindow.getSize())
    return {x, y, width, height}
  }

  shouldAddCustomTitleBar () {
    return (
      !this.isSpec &&
      process.platform === 'darwin' &&
      this.atomApplication.config.get('core.titleBar') === 'custom'
    )
  }

  shouldAddCustomInsetTitleBar () {
    return (
      !this.isSpec &&
      process.platform === 'darwin' &&
      this.atomApplication.config.get('core.titleBar') === 'custom-inset'
    )
  }

  shouldHideTitleBar () {
    return (
      !this.isSpec &&
      process.platform === 'darwin' &&
      this.atomApplication.config.get('core.titleBar') === 'hidden'
    )
  }

  close () {
    return this.browserWindow.close()
  }

  focus () {
    return this.browserWindow.focus()
  }

  minimize () {
    return this.browserWindow.minimize()
  }

  maximize () {
    return this.browserWindow.maximize()
  }

  unmaximize () {
    return this.browserWindow.unmaximize()
  }

  restore () {
    return this.browserWindow.restore()
  }

  setFullScreen (fullScreen) {
    return this.browserWindow.setFullScreen(fullScreen)
  }

  setAutoHideMenuBar (autoHideMenuBar) {
    return this.browserWindow.setAutoHideMenuBar(autoHideMenuBar)
  }

  handlesAtomCommands () {
    return !this.isSpecWindow() && this.isWebViewFocused()
  }

  isFocused () {
    return this.browserWindow.isFocused()
  }

  isMaximized () {
    return this.browserWindow.isMaximized()
  }

  isMinimized () {
    return this.browserWindow.isMinimized()
  }

  isWebViewFocused () {
    return this.browserWindow.isWebViewFocused()
  }

  isSpecWindow () {
    return this.isSpec
  }

  reload () {
    this.loadedPromise = new Promise(resolve => { this.resolveLoadedPromise = resolve })
    this.prepareToUnload().then(canUnload => {
      if (canUnload) this.browserWindow.reload()
    })
    return this.loadedPromise
  }

  showSaveDialog (options, callback) {
    options = Object.assign({
      title: 'Save File',
      defaultPath: this.representedDirectoryPaths[0]
    }, options)

    if (typeof callback === 'function') {
      // Async
      dialog.showSaveDialog(this.browserWindow, options, callback)
    } else {
      // Sync
      return dialog.showSaveDialog(this.browserWindow, options)
    }
  }

  toggleDevTools () {
    return this.browserWindow.toggleDevTools()
  }

  openDevTools () {
    return this.browserWindow.openDevTools()
  }

  closeDevTools () {
    return this.browserWindow.closeDevTools()
  }

  setDocumentEdited (documentEdited) {
    return this.browserWindow.setDocumentEdited(documentEdited)
  }

  setRepresentedFilename (representedFilename) {
    return this.browserWindow.setRepresentedFilename(representedFilename)
  }

  setRepresentedDirectoryPaths (representedDirectoryPaths) {
    this.representedDirectoryPaths = representedDirectoryPaths
    this.representedDirectoryPaths.sort()
    this.loadSettings.initialPaths = this.representedDirectoryPaths
    return this.atomApplication.saveCurrentWindowOptions()
  }

  didClosePathWithWaitSession (path) {
    this.atomApplication.windowDidClosePathWithWaitSession(this, path)
  }

  copy () {
    return this.browserWindow.copy()
  }

  disableZoom () {
    return this.browserWindow.webContents.setVisualZoomLevelLimits(1, 1)
  }
}
<|endoftext|>


<commit_before>/* eslint-disable no-var */
var entryModel = require('./entry')
var dropOne = require('./lib/dropOne')
var forward = require('./lib/forward')
var forwardOne = require('./lib/forwardOne')

var forwardEntry = forwardOne(entryModel, function (entry, ev) {
  return entry._id === ev.data.entryId
})

exports.forward = forward({
  location_entry_changed: forwardEntry,
  location_entry_comment_created: forwardEntry,
  location_entry_comment_changed: forwardEntry,
  location_entry_comment_removed: forwardEntry,

  location_entry_created: function (entries, ev) {
    entries.unshift(ev.data.entry)
  },

  location_entry_removed: function (entries, ev) {
    dropOne(entries, function (entry) {
      return entry._id === ev.data.entryId
    })
  }
})

exports.getAttachments = function (entries) {
  // Return all attachments as array
  //
  var i
  var atts = []

  for (i = 0; i < entries.length; i += 1) {
    atts = atts.concat(entries[i].attachments)
  }

  return atts
}

exports.getImageEntries = function (entries) {
  // Return entries that have one or more image attachments.
  //
  var i, j, att
  var imgEnts = []
  var HEAD = 6

  for (i = 0; i < entries.length; i += 1) {
    for (j = 0; j < entries[i].attachments.length; j += 1) {
      att = entries[i].attachments[j]
      if (att.mimetype.substr(0, HEAD) === 'image/') {
        imgEnts.push(entries[i])
        break // break inner loop
      }
    }
  }

  return imgEnts
}
<commit_msg>entriesModel.getImages<commit_after>/* eslint-disable no-var */
var entryModel = require('./entry')
var attachmentsModel = require('./attachments')
var dropOne = require('./lib/dropOne')
var forward = require('./lib/forward')
var forwardOne = require('./lib/forwardOne')

var forwardEntry = forwardOne(entryModel, function (entry, ev) {
  return entry._id === ev.data.entryId
})

exports.forward = forward({
  location_entry_changed: forwardEntry,
  location_entry_comment_created: forwardEntry,
  location_entry_comment_changed: forwardEntry,
  location_entry_comment_removed: forwardEntry,

  location_entry_created: function (entries, ev) {
    entries.unshift(ev.data.entry)
  },

  location_entry_removed: function (entries, ev) {
    dropOne(entries, function (entry) {
      return entry._id === ev.data.entryId
    })
  }
})

exports.getAttachments = function (entries) {
  // Return all attachments as array
  //
  var i
  var atts = []

  for (i = 0; i < entries.length; i += 1) {
    atts = atts.concat(entries[i].attachments)
  }

  return atts
}

exports.getImages = function (entries) {
  // Return all image attachments as array
  //
  var atts = exports.getAttachments(entries)
  return attachmentsModel.getImages(atts);
}

exports.getImageEntries = function (entries) {
  // Return entries that have one or more image attachments.
  //
  var i, j, att
  var imgEnts = []
  var HEAD = 6

  for (i = 0; i < entries.length; i += 1) {
    for (j = 0; j < entries[i].attachments.length; j += 1) {
      att = entries[i].attachments[j]
      if (att.mimetype.substr(0, HEAD) === 'image/') {
        imgEnts.push(entries[i])
        break // break inner loop
      }
    }
  }

  return imgEnts
}
<|endoftext|>


<commit_before>import React, { PropTypes } from 'react';
import cx from 'classnames';

import Control from '../Control/Control';
import ControlGroup from '../ControlGroup/ControlGroup';
import ControlIcon from '../Control/ControlIcon';

import css from './ControlLayer.css';

const ControlLayer = (props) => {
  const {
    children,
    className,
    controlGroupClassName,
    onZoomIn,
    onZoomOut,
  } = props;

  return (

      <ControlGroup className={ cx(css.controlGroup, controlGroupClassName) }>
        <Control onClick={ onZoomIn }>
          <ControlIcon name="plus" />
        </Control>
        <Control onClick={ onZoomOut }>
          <ControlIcon name="minus" />
        </Control>
      </ControlGroup>
      { children }

  );
};

ControlLayer.propTypes = {
  className: PropTypes.string,
  controlGroupClassName: PropTypes.string,
  children: PropTypes.element.isRequired,
  onZoomIn: PropTypes.func.isRequired,
  onZoomOut: PropTypes.func.isRequired,
};

export default ControlLayer;
<commit_msg>Fix `<ControlLayer />` not accepting multiple children<commit_after>import React, { PropTypes } from 'react';
import cx from 'classnames';

import Control from '../Control/Control';
import ControlGroup from '../ControlGroup/ControlGroup';
import ControlIcon from '../Control/ControlIcon';

import css from './ControlLayer.css';

const ControlLayer = (props) => {
  const {
    children,
    className,
    controlGroupClassName,
    onZoomIn,
    onZoomOut,
  } = props;

  return (

      <ControlGroup className={ cx(css.controlGroup, controlGroupClassName) }>
        <Control onClick={ onZoomIn }>
          <ControlIcon name="plus" />
        </Control>
        <Control onClick={ onZoomOut }>
          <ControlIcon name="minus" />
        </Control>
      </ControlGroup>
      { children }

  );
};

ControlLayer.propTypes = {
  className: PropTypes.string,
  controlGroupClassName: PropTypes.string,
  children: PropTypes.node.isRequired,
  onZoomIn: PropTypes.func.isRequired,
  onZoomOut: PropTypes.func.isRequired,
};

export default ControlLayer;
<|endoftext|>


<commit_before>import Overlay from 'ol/Overlay';
export default {
  template: require('./partials/feature-popup.html'),
  controller: function (
    $scope,
    HsQueryBaseService,
    HsMapService,
    HsQueryVectorService,
    $element,
    HsConfirmDialogService,
    gettext
  ) {
    'ngInject';
    angular.extend($scope, {
      queryBaseService: HsQueryBaseService,
      vectorService: HsQueryVectorService,
      popupVisible() {
        return {
          'visibility':
            HsQueryBaseService.featuresUnderMouse.length > 0
              ? 'visible'
              : 'hidden',
        };
      },

      isClustered(feature) {
        return feature.get('features') && feature.get('features').length > 0;
      },

      serializeFeatureName(feature) {
        if (feature.get('name')) {
          return feature.get('name');
        }
        if (feature.get('title')) {
          return feature.get('title');
        }
        if (feature.get('label')) {
          return feature.get('label');
        }
      },

      isFeatureRemovable(feature) {
        return HsQueryVectorService.isFeatureRemovable(feature);
      },

      isLayerEditable(layer) {
        return HsQueryVectorService.isLayerEditable(layer);
      },

      async removeFeature(feature) {
        const confirmed = await HsConfirmDialogService.show(
          gettext('Really delete this feature?'),
          gettext('Confirm delete')
        );
        if (confirmed == 'yes') {
          HsQueryVectorService.removeFeature(feature);
          HsQueryBaseService.featuresUnderMouse = [];
        }
      },

      async clearLayer(layer) {
        const confirmed = await HsConfirmDialogService.show(
          gettext('Really delete all features from layer "{0}"?').replace(
            '{0}',
            layer.get('title')
          ),
          gettext('Confirm delete')
        );
        if (confirmed == 'yes') {
          if (layer.getSource().getSource) {
            //Clear clustered?
            layer.getSource().getSource().clear();
          }
          layer.getSource().clear();
          HsQueryBaseService.featuresUnderMouse = [];
        }
      },
    });

    const hoverPopupElement = $element[0];
    HsQueryBaseService.hoverPopup = new Overlay({
      element: hoverPopupElement,
    });

    $scope.$on('map.loaded', (e) => {
      HsMapService.map.addOverlay(HsQueryBaseService.hoverPopup);
    });
  },
};
<commit_msg>Defer Injection of confirm dialog service because of lazy bootstrap See: https://stackoverflow.com/questions/47189917/using-downgrademodule-in-conjunction-with-downgradeinjectable-in-an-angular-an/51175142<commit_after>import Overlay from 'ol/Overlay';
export default {
  template: require('./partials/feature-popup.html'),
  controller: function (
    $scope,
    HsQueryBaseService,
    HsMapService,
    HsQueryVectorService,
    $element,
    gettext,
    $injector
  ) {
    'ngInject';
    angular.extend($scope, {
      queryBaseService: HsQueryBaseService,
      vectorService: HsQueryVectorService,
      popupVisible() {
        return {
          'visibility':
            HsQueryBaseService.featuresUnderMouse.length > 0
              ? 'visible'
              : 'hidden',
        };
      },

      isClustered(feature) {
        return feature.get('features') && feature.get('features').length > 0;
      },

      serializeFeatureName(feature) {
        if (feature.get('name')) {
          return feature.get('name');
        }
        if (feature.get('title')) {
          return feature.get('title');
        }
        if (feature.get('label')) {
          return feature.get('label');
        }
      },

      isFeatureRemovable(feature) {
        return HsQueryVectorService.isFeatureRemovable(feature);
      },

      isLayerEditable(layer) {
        return HsQueryVectorService.isLayerEditable(layer);
      },

      async removeFeature(feature) {
        const dialog = $injector.get('HsConfirmDialog');
        const confirmed = await dialog.show(
          gettext('Really delete this feature?'),
          gettext('Confirm delete')
        );
        if (confirmed == 'yes') {
          HsQueryVectorService.removeFeature(feature);
          HsQueryBaseService.featuresUnderMouse = [];
        }
      },

      async clearLayer(layer) {
        const dialog = $injector.get('HsConfirmDialog');
        const confirmed = await dialog.show(
          gettext('Really delete all features from layer "{0}"?').replace(
            '{0}',
            layer.get('title')
          ),
          gettext('Confirm delete')
        );
        if (confirmed == 'yes') {
          if (layer.getSource().getSource) {
            //Clear clustered?
            layer.getSource().getSource().clear();
          }
          layer.getSource().clear();
          HsQueryBaseService.featuresUnderMouse = [];
        }
      },
    });

    const hoverPopupElement = $element[0];
    HsQueryBaseService.hoverPopup = new Overlay({
      element: hoverPopupElement,
    });

    $scope.$on('map.loaded', (e) => {
      HsMapService.map.addOverlay(HsQueryBaseService.hoverPopup);
    });
  },
};
<|endoftext|>