<commit_before>goog.provide('ol.coordinate');

goog.require('ol.math');
goog.require('ol.string');

/**
 * Add `delta` to `coordinate`. `coordinate` is modified in place and returned
 * by the function.
 *
 * Example:
 *
 *     var coord = [7.85, 47.983333];
 *     ol.coordinate.add(coord, [-2, 4]);
 *     // coord is now [5.85, 51.983333]
 *
 * @param {ol.Coordinate} coordinate Coordinate.
 * @param {ol.Coordinate} delta Delta.
 * @return {ol.Coordinate} The input coordinate adjusted by the given delta.
 * @api stable
 */
ol.coordinate.add = function(coordinate, delta) {
  coordinate[0] += delta[0];
  coordinate[1] += delta[1];
  return coordinate;
};

/**
 * Calculates the point closest to the passed coordinate on the passed segment.
 * This is the foot of the perpendicular of the coordinate to the segment when
 * the foot is on the segment, or the closest segment coordinate when the foot
 * is outside the segment.
 *
 * @param {ol.Coordinate} coordinate The coordinate.
 * @param {Array.<ol.Coordinate>} segment The two coordinates of the segment.
 * @return {ol.Coordinate} The foot of the perpendicular of the coordinate to
 *     the segment.
 */
ol.coordinate.closestOnSegment = function(coordinate, segment) {
  var x0 = coordinate[0];
  var y0 = coordinate[1];
  var start = segment[0];
  var end = segment[1];
  var x1 = start[0];
  var y1 = start[1];
  var x2 = end[0];
  var y2 = end[1];
  var dx = x2 - x1;
  var dy = y2 - y1;
  var along = (dx === 0 && dy === 0) ? 0 :
      ((dx * (x0 - x1)) + (dy * (y0 - y1))) / ((dx * dx + dy * dy) || 0);
  var x, y;
  if (along <= 0) {
    x = x1;
    y = y1;
  } else if (along >= 1) {
    x = x2;
    y = y2;
  } else {
    x = x1 + along * dx;
    y = y1 + along * dy;
  }
  return [x, y];
};

/**
 * Returns a {@link ol.CoordinateFormatType} function that can be used to format
 * a {ol.Coordinate} to a string.
 *
 * Example without specifying the fractional digits:
 *
 *     var coord = [7.85, 47.983333];
 *     var stringifyFunc = ol.coordinate.createStringXY();
 *     var out = stringifyFunc(coord);
 *     // out is now '8, 48'
 *
 * Example with explicitly specifying 2 fractional digits:
 *
 *     var coord = [7.85, 47.983333];
 *     var stringifyFunc = ol.coordinate.createStringXY(2);
 *     var out = stringifyFunc(coord);
 *     // out is now '7.85, 47.98'
 *
 * @param {number=} opt_fractionDigits The number of digits to include
 *    after the decimal point. Default is `0`.
 * @return {ol.CoordinateFormatType} Coordinate format.
 * @api stable
 */
ol.coordinate.createStringXY = function(opt_fractionDigits) {
  return (
      /**
       * @param {ol.Coordinate|undefined} coordinate Coordinate.
       * @return {string} String XY.
       */
      function(coordinate) {
        return ol.coordinate.toStringXY(coordinate, opt_fractionDigits);
      });
};

/**
 * @private
 * @param {number} degrees Degrees.
 * @param {string} hemispheres Hemispheres.
 * @param {number=} opt_fractionDigits The number of digits to include
 *    after the decimal point. Default is `0`.
 * @return {string} String.
 */
ol.coordinate.degreesToStringHDMS_ = function(degrees, hemispheres, opt_fractionDigits) {
  var normalizedDegrees = ol.math.modulo(degrees + 180, 360) - 180;
  var x = Math.abs(3600 * normalizedDegrees);
  var dflPrecision = opt_fractionDigits || 0;

  var deg = Math.floor(x / 3600);
  var min = Math.floor((x / 60) % 60);
  var precision = Math.pow(10, dflPrecision);
  var sec = Math.ceil((x * precision) % 60) / precision;

  if (60 === sec) {
    sec = 0;
    min += 1;
  }

  if (min >= 60) {
    min -= 60;
    deg = deg >= 0 ? deg + 1 : deg - 1;
  }

  return deg + '\u00b0 ' + ol.string.padNumber(min, 2) + '\u2032 ' +
    ol.string.padNumber(sec, 2, dflPrecision) + '\u2033 ' +
    hemispheres.charAt(normalizedDegrees < 0 ? 1 : 0);
};

/**
 * Transforms the given {@link ol.Coordinate} to a string using the given string
 * template. The strings `{x}` and `{y}` in the template will be replaced with
 * the first and second coordinate values respectively.
 *
 * Example without specifying the fractional digits:
 *
 *     var coord = [7.85, 47.983333];
 *     var template = 'Coordinate is ({x}|{y}).';
 *     var out = ol.coordinate.format(coord, template);
 *     // out is now 'Coordinate is (8|48).'
 *
 * Example explicitly specifying the fractional digits:
 *
 *     var coord = [7.85, 47.983333];
 *     var template = 'Coordinate is ({x}|{y}).';
 *     var out = ol.coordinate.format(coord, template, 2);
 *     // out is now 'Coordinate is (7.85|47.98).'
 *
 * @param {ol.Coordinate|undefined} coordinate Coordinate.
 * @param {string} template A template string with `{x}` and `{y}` placeholders
 *     that will be replaced by first and second coordinate values.
 * @param {number=} opt_fractionDigits The number of digits to include
 *    after the decimal point. Default is `0`.
 * @return {string} Formatted coordinate.
 * @api stable
 */
ol.coordinate.format = function(coordinate, template, opt_fractionDigits) {
  if (coordinate) {
    return template
      .replace('{x}', coordinate[0].toFixed(opt_fractionDigits))
      .replace('{y}', coordinate[1].toFixed(opt_fractionDigits));
  } else {
    return '';
  }
};

/**
 * @param {ol.Coordinate} coordinate1 First coordinate.
 * @param {ol.Coordinate} coordinate2 Second coordinate.
 * @return {boolean} Whether the passed coordinates are equal.
 */
ol.coordinate.equals = function(coordinate1, coordinate2) {
  var equals = true;
  for (var i = coordinate1.length - 1; i >= 0; --i) {
    if (coordinate1[i] != coordinate2[i]) {
      equals = false;
      break;
    }
  }
  return equals;
};

/**
 * Rotate `coordinate` by `angle`. `coordinate` is modified in place and
 * returned by the function.
 *
 * Example:
 *
 *     var coord = [7.85, 47.983333];
 *     var rotateRadians = Math.PI / 2; // 90 degrees
 *     ol.coordinate.rotate(coord, rotateRadians);
 *     // coord is now [-47.983333, 7.85]
 *
 * @param {ol.Coordinate} coordinate Coordinate.
 * @param {number} angle Angle in radian.
 * @return {ol.Coordinate} Coordinate.
 * @api stable
 */
ol.coordinate.rotate = function(coordinate, angle) {
  var cosAngle = Math.cos(angle);
  var sinAngle = Math.sin(angle);
  var x = coordinate[0] * cosAngle - coordinate[1] * sinAngle;
  var y = coordinate[1] * cosAngle + coordinate[0] * sinAngle;
  coordinate[0] = x;
  coordinate[1] = y;
  return coordinate;
};

/**
 * Scale `coordinate` by `scale`. `coordinate` is modified in place and returned
 * by the function.
 *
 * Example:
 *
 *     var coord = [7.85, 47.983333];
 *     var scale = 1.2;
 *     ol.coordinate.scale(coord, scale);
 *     // coord is now [9.42, 57.5799996]
 *
 * @param {ol.Coordinate} coordinate Coordinate.
 * @param {number} scale Scale factor.
 * @return {ol.Coordinate} Coordinate.
 */
ol.coordinate.scale = function(coordinate, scale) {
  coordinate[0] *= scale;
  coordinate[1] *= scale;
  return coordinate;
};

/**
 * Subtract `delta` to `coordinate`. `coordinate` is modified in place and
 * returned by the function.
 *
 * @param {ol.Coordinate} coordinate Coordinate.
 * @param {ol.Coordinate} delta Delta.
 * @return {ol.Coordinate} Coordinate.
 */
ol.coordinate.sub = function(coordinate, delta) {
  coordinate[0] -= delta[0];
  coordinate[1] -= delta[1];
  return coordinate;
};

/**
 * @param {ol.Coordinate} coord1 First coordinate.
 * @param {ol.Coordinate} coord2 Second coordinate.
 * @return {number} Squared distance between coord1 and coord2.
 */
ol.coordinate.squaredDistance = function(coord1, coord2) {
  var dx = coord1[0] - coord2[0];
  var dy = coord1[1] - coord2[1];
  return dx * dx + dy * dy;
};

/**
 * Calculate the squared distance from a coordinate to a line segment.
 *
 * @param {ol.Coordinate} coordinate Coordinate of the point.
 * @param {Array.<ol.Coordinate>} segment Line segment (2 coordinates).
 * @return {number} Squared distance from the point to the line segment.
 */
ol.coordinate.squaredDistanceToSegment = function(coordinate, segment) {
  return ol.coordinate.squaredDistance(coordinate,
      ol.coordinate.closestOnSegment(coordinate, segment));
};

/**
 * Format a geographic coordinate with the hemisphere, degrees, minutes, and
 * seconds.
 *
 * Example without specifying fractional digits:
 *
 *     var coord = [7.85, 47.983333];
 *     var out = ol.coordinate.toStringHDMS(coord);
 *     // out is now '47° 58′ 60″ N 7° 50′ 60″ E'
 *
 * Example explicitly specifying 1 fractional digit:
 *
 *     var coord = [7.85, 47.983333];
 *     var out = ol.coordinate.toStringHDMS(coord, 1);
 *     // out is now '47° 58′ 60.0″ N 7° 50′ 60.0″ E'
 *
 * @param {ol.Coordinate|undefined} coordinate Coordinate.
 * @param {number=} opt_fractionDigits The number of digits to include
 *    after the decimal point. Default is `0`.
 * @return {string} Hemisphere, degrees, minutes and seconds.
 * @api stable
 */
ol.coordinate.toStringHDMS = function(coordinate, opt_fractionDigits) {
  if (coordinate) {
    return ol.coordinate.degreesToStringHDMS_(coordinate[1], 'NS', opt_fractionDigits) + ' ' +
        ol.coordinate.degreesToStringHDMS_(coordinate[0], 'EW', opt_fractionDigits);
  } else {
    return '';
  }
};

/**
 * Format a coordinate as a comma delimited string.
 *
 * Example without specifying fractional digits:
 *
 *     var coord = [7.85, 47.983333];
 *     var out = ol.coordinate.toStringXY(coord);
 *     // out is now '8, 48'
 *
 * Example explicitly specifying 1 fractional digit:
 *
 *     var coord = [7.85, 47.983333];
 *     var out = ol.coordinate.toStringXY(coord, 1);
 *     // out is now '7.8, 48.0'
 *
 * @param {ol.Coordinate|undefined} coordinate Coordinate.
 * @param {number=} opt_fractionDigits The number of digits to include
 *    after the decimal point. Default is `0`.
 * @return {string} XY.
 * @api stable
 */
ol.coordinate.toStringXY = function(coordinate, opt_fractionDigits) {
  return ol.coordinate.format(coordinate, '{x}, {y}', opt_fractionDigits);
};
<commit_msg>fixed the degreesToStringHDMS_() function to promote a seconds value of 60 up to the next minute; fixed calculation of components<commit_after>goog.provide('ol.coordinate');

goog.require('ol.math');
goog.require('ol.string');

/**
 * Add `delta` to `coordinate`. `coordinate` is modified in place and returned
 * by the function.
 *
 * Example:
 *
 *     var coord = [7.85, 47.983333];
 *     ol.coordinate.add(coord, [-2, 4]);
 *     // coord is now [5.85, 51.983333]
 *
 * @param {ol.Coordinate} coordinate Coordinate.
 * @param {ol.Coordinate} delta Delta.
 * @return {ol.Coordinate} The input coordinate adjusted by the given delta.
 * @api stable
 */
ol.coordinate.add = function(coordinate, delta) {
  coordinate[0] += delta[0];
  coordinate[1] += delta[1];
  return coordinate;
};

/**
 * Calculates the point closest to the passed coordinate on the passed segment.
 * This is the foot of the perpendicular of the coordinate to the segment when
 * the foot is on the segment, or the closest segment coordinate when the foot
 * is outside the segment.
 *
 * @param {ol.Coordinate} coordinate The coordinate.
 * @param {Array.<ol.Coordinate>} segment The two coordinates of the segment.
 * @return {ol.Coordinate} The foot of the perpendicular of the coordinate to
 *     the segment.
 */
ol.coordinate.closestOnSegment = function(coordinate, segment) {
  var x0 = coordinate[0];
  var y0 = coordinate[1];
  var start = segment[0];
  var end = segment[1];
  var x1 = start[0];
  var y1 = start[1];
  var x2 = end[0];
  var y2 = end[1];
  var dx = x2 - x1;
  var dy = y2 - y1;
  var along = (dx === 0 && dy === 0) ? 0 :
      ((dx * (x0 - x1)) + (dy * (y0 - y1))) / ((dx * dx + dy * dy) || 0);
  var x, y;
  if (along <= 0) {
    x = x1;
    y = y1;
  } else if (along >= 1) {
    x = x2;
    y = y2;
  } else {
    x = x1 + along * dx;
    y = y1 + along * dy;
  }
  return [x, y];
};

/**
 * Returns a {@link ol.CoordinateFormatType} function that can be used to format
 * a {ol.Coordinate} to a string.
 *
 * Example without specifying the fractional digits:
 *
 *     var coord = [7.85, 47.983333];
 *     var stringifyFunc = ol.coordinate.createStringXY();
 *     var out = stringifyFunc(coord);
 *     // out is now '8, 48'
 *
 * Example with explicitly specifying 2 fractional digits:
 *
 *     var coord = [7.85, 47.983333];
 *     var stringifyFunc = ol.coordinate.createStringXY(2);
 *     var out = stringifyFunc(coord);
 *     // out is now '7.85, 47.98'
 *
 * @param {number=} opt_fractionDigits The number of digits to include
 *    after the decimal point. Default is `0`.
 * @return {ol.CoordinateFormatType} Coordinate format.
 * @api stable
 */
ol.coordinate.createStringXY = function(opt_fractionDigits) {
  return (
      /**
       * @param {ol.Coordinate|undefined} coordinate Coordinate.
       * @return {string} String XY.
       */
      function(coordinate) {
        return ol.coordinate.toStringXY(coordinate, opt_fractionDigits);
      });
};

/**
 * @private
 * @param {number} degrees Degrees.
 * @param {string} hemispheres Hemispheres.
 * @param {number=} opt_fractionDigits The number of digits to include
 *    after the decimal point. Default is `0`.
 * @return {string} String.
 */
ol.coordinate.degreesToStringHDMS_ = function(degrees, hemispheres, opt_fractionDigits) {
  var normalizedDegrees = ol.math.modulo(degrees + 180, 360) - 180;
  var x = Math.abs(3600 * normalizedDegrees);
  var dflPrecision = opt_fractionDigits || 0;
  var precision = Math.pow(10, dflPrecision);

  var deg = Math.floor(x / 3600);
  var min = Math.floor((x - deg * 3600) / 60);
  var sec = x - (deg * 3600) - (min * 60);
  sec = Math.ceil(sec * precision) / precision;

  if (sec >= 60) {
    sec = 0;
    min += 1;
  }

  if (min >= 60) {
    min = 0;
    deg += 1;
  }

  return deg + '\u00b0 ' + ol.string.padNumber(min, 2) + '\u2032 ' +
    ol.string.padNumber(sec, 2, dflPrecision) + '\u2033 ' +
    hemispheres.charAt(normalizedDegrees < 0 ? 1 : 0);
};

/**
 * Transforms the given {@link ol.Coordinate} to a string using the given string
 * template. The strings `{x}` and `{y}` in the template will be replaced with
 * the first and second coordinate values respectively.
 *
 * Example without specifying the fractional digits:
 *
 *     var coord = [7.85, 47.983333];
 *     var template = 'Coordinate is ({x}|{y}).';
 *     var out = ol.coordinate.format(coord, template);
 *     // out is now 'Coordinate is (8|48).'
 *
 * Example explicitly specifying the fractional digits:
 *
 *     var coord = [7.85, 47.983333];
 *     var template = 'Coordinate is ({x}|{y}).';
 *     var out = ol.coordinate.format(coord, template, 2);
 *     // out is now 'Coordinate is (7.85|47.98).'
 *
 * @param {ol.Coordinate|undefined} coordinate Coordinate.
 * @param {string} template A template string with `{x}` and `{y}` placeholders
 *     that will be replaced by first and second coordinate values.
 * @param {number=} opt_fractionDigits The number of digits to include
 *    after the decimal point. Default is `0`.
 * @return {string} Formatted coordinate.
 * @api stable
 */
ol.coordinate.format = function(coordinate, template, opt_fractionDigits) {
  if (coordinate) {
    return template
      .replace('{x}', coordinate[0].toFixed(opt_fractionDigits))
      .replace('{y}', coordinate[1].toFixed(opt_fractionDigits));
  } else {
    return '';
  }
};

/**
 * @param {ol.Coordinate} coordinate1 First coordinate.
 * @param {ol.Coordinate} coordinate2 Second coordinate.
 * @return {boolean} Whether the passed coordinates are equal.
 */
ol.coordinate.equals = function(coordinate1, coordinate2) {
  var equals = true;
  for (var i = coordinate1.length - 1; i >= 0; --i) {
    if (coordinate1[i] != coordinate2[i]) {
      equals = false;
      break;
    }
  }
  return equals;
};

/**
 * Rotate `coordinate` by `angle`. `coordinate` is modified in place and
 * returned by the function.
 *
 * Example:
 *
 *     var coord = [7.85, 47.983333];
 *     var rotateRadians = Math.PI / 2; // 90 degrees
 *     ol.coordinate.rotate(coord, rotateRadians);
 *     // coord is now [-47.983333, 7.85]
 *
 * @param {ol.Coordinate} coordinate Coordinate.
 * @param {number} angle Angle in radian.
 * @return {ol.Coordinate} Coordinate.
 * @api stable
 */
ol.coordinate.rotate = function(coordinate, angle) {
  var cosAngle = Math.cos(angle);
  var sinAngle = Math.sin(angle);
  var x = coordinate[0] * cosAngle - coordinate[1] * sinAngle;
  var y = coordinate[1] * cosAngle + coordinate[0] * sinAngle;
  coordinate[0] = x;
  coordinate[1] = y;
  return coordinate;
};

/**
 * Scale `coordinate` by `scale`. `coordinate` is modified in place and returned
 * by the function.
 *
 * Example:
 *
 *     var coord = [7.85, 47.983333];
 *     var scale = 1.2;
 *     ol.coordinate.scale(coord, scale);
 *     // coord is now [9.42, 57.5799996]
 *
 * @param {ol.Coordinate} coordinate Coordinate.
 * @param {number} scale Scale factor.
 * @return {ol.Coordinate} Coordinate.
 */
ol.coordinate.scale = function(coordinate, scale) {
  coordinate[0] *= scale;
  coordinate[1] *= scale;
  return coordinate;
};

/**
 * Subtract `delta` to `coordinate`. `coordinate` is modified in place and
 * returned by the function.
 *
 * @param {ol.Coordinate} coordinate Coordinate.
 * @param {ol.Coordinate} delta Delta.
 * @return {ol.Coordinate} Coordinate.
 */
ol.coordinate.sub = function(coordinate, delta) {
  coordinate[0] -= delta[0];
  coordinate[1] -= delta[1];
  return coordinate;
};

/**
 * @param {ol.Coordinate} coord1 First coordinate.
 * @param {ol.Coordinate} coord2 Second coordinate.
 * @return {number} Squared distance between coord1 and coord2.
 */
ol.coordinate.squaredDistance = function(coord1, coord2) {
  var dx = coord1[0] - coord2[0];
  var dy = coord1[1] - coord2[1];
  return dx * dx + dy * dy;
};

/**
 * Calculate the squared distance from a coordinate to a line segment.
 *
 * @param {ol.Coordinate} coordinate Coordinate of the point.
 * @param {Array.<ol.Coordinate>} segment Line segment (2 coordinates).
 * @return {number} Squared distance from the point to the line segment.
 */
ol.coordinate.squaredDistanceToSegment = function(coordinate, segment) {
  return ol.coordinate.squaredDistance(coordinate,
      ol.coordinate.closestOnSegment(coordinate, segment));
};

/**
 * Format a geographic coordinate with the hemisphere, degrees, minutes, and
 * seconds.
 *
 * Example without specifying fractional digits:
 *
 *     var coord = [7.85, 47.983333];
 *     var out = ol.coordinate.toStringHDMS(coord);
 *     // out is now '47° 58′ 60″ N 7° 50′ 60″ E'
 *
 * Example explicitly specifying 1 fractional digit:
 *
 *     var coord = [7.85, 47.983333];
 *     var out = ol.coordinate.toStringHDMS(coord, 1);
 *     // out is now '47° 58′ 60.0″ N 7° 50′ 60.0″ E'
 *
 * @param {ol.Coordinate|undefined} coordinate Coordinate.
 * @param {number=} opt_fractionDigits The number of digits to include
 *    after the decimal point. Default is `0`.
 * @return {string} Hemisphere, degrees, minutes and seconds.
 * @api stable
 */
ol.coordinate.toStringHDMS = function(coordinate, opt_fractionDigits) {
  if (coordinate) {
    return ol.coordinate.degreesToStringHDMS_(coordinate[1], 'NS', opt_fractionDigits) + ' ' +
        ol.coordinate.degreesToStringHDMS_(coordinate[0], 'EW', opt_fractionDigits);
  } else {
    return '';
  }
};

/**
 * Format a coordinate as a comma delimited string.
 *
 * Example without specifying fractional digits:
 *
 *     var coord = [7.85, 47.983333];
 *     var out = ol.coordinate.toStringXY(coord);
 *     // out is now '8, 48'
 *
 * Example explicitly specifying 1 fractional digit:
 *
 *     var coord = [7.85, 47.983333];
 *     var out = ol.coordinate.toStringXY(coord, 1);
 *     // out is now '7.8, 48.0'
 *
 * @param {ol.Coordinate|undefined} coordinate Coordinate.
 * @param {number=} opt_fractionDigits The number of digits to include
 *    after the decimal point. Default is `0`.
 * @return {string} XY.
 * @api stable
 */
ol.coordinate.toStringXY = function(coordinate, opt_fractionDigits) {
  return ol.coordinate.format(coordinate, '{x}, {y}', opt_fractionDigits);
};
<|endoftext|>


<commit_before>// FIXME add rotation

goog.provide('ol.render.Box');

goog.require('goog.Disposable');
goog.require('goog.asserts');
goog.require('goog.events');
goog.require('ol.geom.Polygon');
goog.require('ol.render.EventType');
goog.require('ol.style.Fill');
goog.require('ol.style.Style');

/**
 * @constructor
 * @extends {goog.Disposable}
 * @param {ol.style.Style=} opt_style Style.
 */
ol.render.Box = function(opt_style) {

  /**
   * @private
   * @type {ol.Map}
   */
  this.map_ = null;

  /**
   * @private
   * @type {goog.events.Key}
   */
  this.postComposeListenerKey_ = null;

  /**
   * @private
   * @type {ol.Coordinate}
   */
  this.startCoordinate_ = null;

  /**
   * @private
   * @type {ol.Coordinate}
   */
  this.endCoordinate_ = null;

  /**
   * @private
   * @type {ol.geom.Polygon}
   */
  this.geometry_ = null;

  /**
   * @private
   * @type {ol.style.Style}
   */
  this.style_ = goog.isDef(opt_style) ? opt_style : new ol.style.Style({
    fill: new ol.style.Fill({
      color: 'rgba(0,0,0,0.5)'
    }),
    image: null,
    stroke: null,
    text: null,
    zIndex: 0
  });

};
goog.inherits(ol.render.Box, goog.Disposable);

/**
 * @private
 * @param {ol.Extent} extent Extent.
 * @return {ol.geom.Polygon} Geometry.
 */
ol.render.Box.prototype.createGeometry_ = function(extent) {
  goog.asserts.assert(!goog.isNull(this.startCoordinate_));
  goog.asserts.assert(!goog.isNull(this.endCoordinate_));
  var startCoordinate = this.startCoordinate_;
  var endCoordinate = this.endCoordinate_;
  var coordinates = [
    // outer ring
    [
      [extent[0], extent[1]],
      [extent[0], extent[3]],
      [extent[2], extent[3]],
      [extent[2], extent[1]]
    ],
    // inner ring
    [
      startCoordinate,
      [startCoordinate[0], endCoordinate[1]],
      endCoordinate,
      [endCoordinate[0], startCoordinate[1]]
    ]
  ];
  return new ol.geom.Polygon(coordinates);
};

/**
 * @inheritDoc
 */
ol.render.Box.prototype.disposeInternal = function() {
  this.setMap(null);
};

/**
 * @param {ol.render.Event} event Event.
 * @private
 */
ol.render.Box.prototype.handleMapPostCompose_ = function(event) {
  var extent = event.getFrameState().extent;
  this.geometry_ = this.createGeometry_(extent);
  var style = this.style_;
  var render = event.getRender();
  render.setFillStrokeStyle(style.getFill(), style.getStroke());
  render.drawPolygonGeometry(this.geometry_, null);
};

/**
 * @return {ol.geom.Polygon} Geometry.
 */
ol.render.Box.prototype.getGeometry = function() {
  return this.geometry_;
};

/**
 * @private
 */
ol.render.Box.prototype.requestMapRenderFrame_ = function() {
  if (!goog.isNull(this.map_) &&
      !goog.isNull(this.startCoordinate_) &&
      !goog.isNull(this.endCoordinate_)) {
    this.map_.requestRenderFrame();
  }
};

/**
 * @param {ol.Map} map Map.
 */
ol.render.Box.prototype.setMap = function(map) {
  if (!goog.isNull(this.postComposeListenerKey_)) {
    goog.events.unlistenByKey(this.postComposeListenerKey_);
    this.postComposeListenerKey_ = null;
    this.map_.requestRenderFrame();
    this.map_ = null;
  }
  this.map_ = map;
  if (!goog.isNull(this.map_)) {
    this.postComposeListenerKey_ = goog.events.listen(
        map, ol.render.EventType.POSTCOMPOSE, this.handleMapPostCompose_, false,
        this);
    this.requestMapRenderFrame_();
  }
};

/**
 * @param {ol.Coordinate} startCoordinate Start coordinate.
 * @param {ol.Coordinate} endCoordinate End coordinate.
 */
ol.render.Box.prototype.setCoordinates =
    function(startCoordinate, endCoordinate) {
  this.startCoordinate_ = startCoordinate;
  this.endCoordinate_ = endCoordinate;
  this.requestMapRenderFrame_();
};
<commit_msg>No default style in ol.render.Box<commit_after>// FIXME add rotation

goog.provide('ol.render.Box');

goog.require('goog.Disposable');
goog.require('goog.asserts');
goog.require('goog.events');
goog.require('ol.geom.Polygon');
goog.require('ol.render.EventType');

/**
 * @constructor
 * @extends {goog.Disposable}
 * @param {ol.style.Style} style Style.
 */
ol.render.Box = function(style) {

  /**
   * @private
   * @type {ol.Map}
   */
  this.map_ = null;

  /**
   * @private
   * @type {goog.events.Key}
   */
  this.postComposeListenerKey_ = null;

  /**
   * @private
   * @type {ol.Coordinate}
   */
  this.startCoordinate_ = null;

  /**
   * @private
   * @type {ol.Coordinate}
   */
  this.endCoordinate_ = null;

  /**
   * @private
   * @type {ol.geom.Polygon}
   */
  this.geometry_ = null;

  /**
   * @private
   * @type {ol.style.Style}
   */
  this.style_ = style;

};
goog.inherits(ol.render.Box, goog.Disposable);

/**
 * @private
 * @param {ol.Extent} extent Extent.
 * @return {ol.geom.Polygon} Geometry.
 */
ol.render.Box.prototype.createGeometry_ = function(extent) {
  goog.asserts.assert(!goog.isNull(this.startCoordinate_));
  goog.asserts.assert(!goog.isNull(this.endCoordinate_));
  var startCoordinate = this.startCoordinate_;
  var endCoordinate = this.endCoordinate_;
  var coordinates = [
    // outer ring
    [
      [extent[0], extent[1]],
      [extent[0], extent[3]],
      [extent[2], extent[3]],
      [extent[2], extent[1]]
    ],
    // inner ring
    [
      startCoordinate,
      [startCoordinate[0], endCoordinate[1]],
      endCoordinate,
      [endCoordinate[0], startCoordinate[1]]
    ]
  ];
  return new ol.geom.Polygon(coordinates);
};

/**
 * @inheritDoc
 */
ol.render.Box.prototype.disposeInternal = function() {
  this.setMap(null);
};

/**
 * @param {ol.render.Event} event Event.
 * @private
 */
ol.render.Box.prototype.handleMapPostCompose_ = function(event) {
  var extent = event.getFrameState().extent;
  this.geometry_ = this.createGeometry_(extent);
  var style = this.style_;
  goog.asserts.assert(!goog.isNull(style));
  var render = event.getRender();
  render.setFillStrokeStyle(style.getFill(), style.getStroke());
  render.drawPolygonGeometry(this.geometry_, null);
};

/**
 * @return {ol.geom.Polygon} Geometry.
 */
ol.render.Box.prototype.getGeometry = function() {
  return this.geometry_;
};

/**
 * @private
 */
ol.render.Box.prototype.requestMapRenderFrame_ = function() {
  if (!goog.isNull(this.map_) &&
      !goog.isNull(this.startCoordinate_) &&
      !goog.isNull(this.endCoordinate_)) {
    this.map_.requestRenderFrame();
  }
};

/**
 * @param {ol.Map} map Map.
 */
ol.render.Box.prototype.setMap = function(map) {
  if (!goog.isNull(this.postComposeListenerKey_)) {
    goog.events.unlistenByKey(this.postComposeListenerKey_);
    this.postComposeListenerKey_ = null;
    this.map_.requestRenderFrame();
    this.map_ = null;
  }
  this.map_ = map;
  if (!goog.isNull(this.map_)) {
    this.postComposeListenerKey_ = goog.events.listen(
        map, ol.render.EventType.POSTCOMPOSE, this.handleMapPostCompose_, false,
        this);
    this.requestMapRenderFrame_();
  }
};

/**
 * @param {ol.Coordinate} startCoordinate Start coordinate.
 * @param {ol.Coordinate} endCoordinate End coordinate.
 */
ol.render.Box.prototype.setCoordinates =
    function(startCoordinate, endCoordinate) {
  this.startCoordinate_ = startCoordinate;
  this.endCoordinate_ = endCoordinate;
  this.requestMapRenderFrame_();
};
<|endoftext|>


<commit_before>var Stream = require("./stream.js").Stream;
var Lexer  = require("./lexer.js").Lexer;
var Node   = require("./node.js").Node;
var assert = require("assert");

function Parser() {
}

Parser.prototype = {
  initStatus: function (stream) {
    if (typeof stream === "string")
      stream = new Stream(stream);
    this.lexer = new Lexer(stream);
    this.document = [];
  },

  parse: function (stream) {
    this.initStatus(stream);
    this.parseDocument();
    return this.document;
  },

  skipBlank: function () {
    var blankToken = null;
    while (this.lexer.peekNextToken().type === Lexer.tokens.blank)
      blankToken = this.lexer.getNextToken();
    return blankToken;
  },

  // ------------------------------------------------------------
  // <Document> ::= <Element>*
  // ------------------------------------------------------------

  parseDocument: function () {
    while (this.lexer.hasNext()) {
      var element = this.parseElement();
      if (element) this.document.push(element);
    }
  },

  // ------------------------------------------------------------
  // <Element> ::= (<Header> | <List>
  //              | <Preformatted> | <Paragraph>
  //              | <Table>)*
  // ------------------------------------------------------------

  parseElement: function () {
    var token = null;

    switch (this.lexer.peekNextToken().type) {
    case Lexer.tokens.header:
      token = this.parseHeader();
      break;
    case Lexer.tokens.preformatted:
      token = this.parsePreformatted();
      break;
    case Lexer.tokens.orderedListElement:
    case Lexer.tokens.unorderedListElement:
      token = this.parseList();
      break;
    case Lexer.tokens.line:
      token = this.parseText();
      break;
    case Lexer.tokens.blank:
      this.skipBlank();
      if (this.lexer.hasNext()) {
        if (this.lexer.peekNextToken().type === Lexer.tokens.line)
          token = this.parseParagraph();
        else
          token = this.parseElement();
      }
      break;
    }

    return token;
  },

  // ------------------------------------------------------------
  // <Header>
  //
  // : preformatted
  // : block
  // ------------------------------------------------------------

  parseHeader: function () {
    var headerToken = this.lexer.getNextToken();
    assert.ok(headerToken.indentation === 0);

    var header = Node.createHeader([
      this.createTextNode(headerToken.content) // TODO: Parse inline markups
    ], { level: headerToken.level });

    return header;
  },

  // ------------------------------------------------------------
  // <Preformatted>
  //
  // : preformatted
  // : block
  // ------------------------------------------------------------

  parsePreformatted: function () {
    var preformattedFirstToken = this.lexer.peekNextToken();
    assert.ok(preformattedFirstToken.type === Lexer.tokens.preformatted);
    var preformatted = Node.createPreformatted([]);

    while (this.lexer.hasNext()) {
      var token = this.lexer.peekNextToken();
      if (token.type !== Lexer.tokens.preformatted ||
          token.indentation < preformattedFirstToken.indentation)
        break;
      this.lexer.getNextToken();
      preformatted.children.push(this.createTextNode(token.content));
    }

    return preformatted;
  },

  // ------------------------------------------------------------
  // <List>
  //
  //  - foo
  //    1. bar
  //    2. baz
  // ------------------------------------------------------------

  parseList: function () {
    var rootToken = this.lexer.peekNextToken();
    var list = rootToken.type === Lexer.tokens.unorderedListElement ?
      Node.createUnorderedList([]) : Node.createOrderedList([]);

    while (this.lexer.hasNext()) {
      var nextToken = this.lexer.peekNextToken();
      if (!nextToken.isListElement() || nextToken.indentation !== rootToken.indentation)
        break;
      list.children.push(this.parseListElement(rootToken.indentation));
    }

    return list;
  },

  parseListElement: function (rootIndentation) {
    var listElementToken = this.lexer.getNextToken();
    assert.ok(listElementToken.isListElement());
    var listElement = Node.createListElement([
      this.createTextNode(listElementToken.content)
    ]);

    while (this.lexer.hasNext()) {
      var blankToken = this.skipBlank();
      if (!this.lexer.hasNext())
        break;

      var notBlankNextToken = this.lexer.peekNextToken();
      if (blankToken && !notBlankNextToken.isListElement())
        this.lexer.pushToken(blankToken); // Recover blank token only when next line is not listElement.
      if (notBlankNextToken.indentation <= rootIndentation)
        break;                  // end of the list

      var element = this.parseElement(); // recursive
      if (element)
        listElement.children.push(element);
    }

    return listElement;
  },

  // ------------------------------------------------------------
  // <Paragraph> ::= <Blank> <Line>*
  // ------------------------------------------------------------

  parseParagraph: function () {
    var paragraphFisrtToken = this.lexer.peekNextToken();
    assert.ok(paragraphFisrtToken.type === Lexer.tokens.line);
    var paragraph = Node.createParagraph([]);

    while (this.lexer.hasNext()) {
      var nextToken = this.lexer.peekNextToken();
      if (nextToken.type !== Lexer.tokens.line
          || nextToken.indentation < paragraphFisrtToken.indentation)
        break;
      this.lexer.getNextToken();
      paragraph.children.push(this.createTextNode(nextToken.content));
    }

    return paragraph;
  },

  parseText: function () {
    var lineToken = this.lexer.getNextToken();
    assert.ok(lineToken.type === Lexer.tokens.line);
    return this.createTextNode(lineToken.content);
  },

  // ------------------------------------------------------------
  // <Text> (DOM Like)
  // ------------------------------------------------------------

  createTextNode: function (text) {
    return Node.createText(null, { value: text });
  }
};

if (typeof exports !== "undefined")
  exports.Parser = Parser;
<commit_msg>newline<commit_after>var Stream = require("./stream.js").Stream;
var Lexer  = require("./lexer.js").Lexer;
var Node   = require("./node.js").Node;
var assert = require("assert");

function Parser() {
}

Parser.prototype = {
  initStatus: function (stream) {
    if (typeof stream === "string")
      stream = new Stream(stream);
    this.lexer = new Lexer(stream);
    this.document = [];
  },

  parse: function (stream) {
    this.initStatus(stream);
    this.parseDocument();
    return this.document;
  },

  skipBlank: function () {
    var blankToken = null;
    while (this.lexer.peekNextToken().type === Lexer.tokens.blank)
      blankToken = this.lexer.getNextToken();
    return blankToken;
  },

  // ------------------------------------------------------------
  // <Document> ::= <Element>*
  // ------------------------------------------------------------

  parseDocument: function () {
    while (this.lexer.hasNext()) {
      var element = this.parseElement();
      if (element) this.document.push(element);
    }
  },

  // ------------------------------------------------------------
  // <Element> ::= (<Header> | <List>
  //              | <Preformatted> | <Paragraph>
  //              | <Table>)*
  // ------------------------------------------------------------

  parseElement: function () {
    var token = null;

    switch (this.lexer.peekNextToken().type) {
    case Lexer.tokens.header:
      token = this.parseHeader();
      break;
    case Lexer.tokens.preformatted:
      token = this.parsePreformatted();
      break;
    case Lexer.tokens.orderedListElement:
    case Lexer.tokens.unorderedListElement:
      token = this.parseList();
      break;
    case Lexer.tokens.line:
      token = this.parseText();
      break;
    case Lexer.tokens.blank:
      this.skipBlank();
      if (this.lexer.hasNext()) {
        if (this.lexer.peekNextToken().type === Lexer.tokens.line)
          token = this.parseParagraph();
        else
          token = this.parseElement();
      }
      break;
    }

    return token;
  },

  // ------------------------------------------------------------
  // <Header>
  //
  // : preformatted
  // : block
  // ------------------------------------------------------------

  parseHeader: function () {
    var headerToken = this.lexer.getNextToken();
    assert.ok(headerToken.indentation === 0);

    var header = Node.createHeader([
      this.createTextNode(headerToken.content) // TODO: Parse inline markups
    ], { level: headerToken.level });

    return header;
  },

  // ------------------------------------------------------------
  // <Preformatted>
  //
  // : preformatted
  // : block
  // ------------------------------------------------------------

  parsePreformatted: function () {
    var preformattedFirstToken = this.lexer.peekNextToken();
    assert.ok(preformattedFirstToken.type === Lexer.tokens.preformatted);
    var preformatted = Node.createPreformatted([]);

    var textContents = [];

    while (this.lexer.hasNext()) {
      var token = this.lexer.peekNextToken();
      if (token.type !== Lexer.tokens.preformatted ||
          token.indentation < preformattedFirstToken.indentation)
        break;
      this.lexer.getNextToken();
      textContents.push(token.content);
    }

    preformatted.children.push(this.createTextNode(textContents.join("\n")));

    return preformatted;
  },

  // ------------------------------------------------------------
  // <List>
  //
  //  - foo
  //    1. bar
  //    2. baz
  // ------------------------------------------------------------

  parseList: function () {
    var rootToken = this.lexer.peekNextToken();
    var list = rootToken.type === Lexer.tokens.unorderedListElement ?
      Node.createUnorderedList([]) : Node.createOrderedList([]);

    while (this.lexer.hasNext()) {
      var nextToken = this.lexer.peekNextToken();
      if (!nextToken.isListElement() || nextToken.indentation !== rootToken.indentation)
        break;
      list.children.push(this.parseListElement(rootToken.indentation));
    }

    return list;
  },

  parseListElement: function (rootIndentation) {
    var listElementToken = this.lexer.getNextToken();
    assert.ok(listElementToken.isListElement());
    var listElement = Node.createListElement([
      this.createTextNode(listElementToken.content)
    ]);

    while (this.lexer.hasNext()) {
      var blankToken = this.skipBlank();
      if (!this.lexer.hasNext())
        break;

      var notBlankNextToken = this.lexer.peekNextToken();
      if (blankToken && !notBlankNextToken.isListElement())
        this.lexer.pushToken(blankToken); // Recover blank token only when next line is not listElement.
      if (notBlankNextToken.indentation <= rootIndentation)
        break;                  // end of the list

      var element = this.parseElement(); // recursive
      if (element)
        listElement.children.push(element);
    }

    return listElement;
  },

  // ------------------------------------------------------------
  // <Paragraph> ::= <Blank> <Line>*
  // ------------------------------------------------------------

  parseParagraph: function () {
    var paragraphFisrtToken = this.lexer.peekNextToken();
    assert.ok(paragraphFisrtToken.type === Lexer.tokens.line);
    var paragraph = Node.createParagraph([]);

    var textContents = [];

    while (this.lexer.hasNext()) {
      var nextToken = this.lexer.peekNextToken();
      if (nextToken.type !== Lexer.tokens.line
          || nextToken.indentation < paragraphFisrtToken.indentation)
        break;
      this.lexer.getNextToken();
      textContents.push(nextToken.content);
    }

    paragraph.children.push(this.createTextNode(textContents.join("\n")));

    return paragraph;
  },

  parseText: function () {
    var lineToken = this.lexer.getNextToken();
    assert.ok(lineToken.type === Lexer.tokens.line);
    return this.createTextNode(lineToken.content);
  },

  // ------------------------------------------------------------
  // <Text> (DOM Like)
  // ------------------------------------------------------------

  createTextNode: function (text) {
    return Node.createText(null, { value: text });
  }
};

if (typeof exports !== "undefined")
  exports.Parser = Parser;
<|endoftext|>


<commit_before>
define( function() {

	var handleSort = function( e, args ) {

		if ( this.isGenius && this.isGenius() ) {

			var field = args.sortCol.field

			, dataView = this.getData();

			dataView.beginUpdate();

			dataView.sort( function( a, b ) {
				var x = a[ field ], y = b[ field ];

				return (x === y ? 0 : (x > y ? 1 : -1));
			}, args.sortAsc );

			dataView.endUpdate();
		}
		else this.getData().onPagingInfoChanged.notify( { doSearch: 1 } );
	};

	return function( $G, ajaxOptions, enable ) {

		/** All operations in server-side */
		var pager;

		if ( true === enable ) {

			var dataView, request, loading;

			if ( !ajaxOptions.serviceName ) {

				throw "Service name cann't be null";
			}

			dataView = $G.getData();
			loading = $( "

" );

			$( $G.getContainerNode() ).append( loading );

			pager = function( pagingInfo, callback, args ) {

				var VO = { wpf_dup_token: +new Date() + Math.random() }

				, request = loading.data( "data-request" );

				args = args || {};

				VO[ ajaxOptions.moduleName || (ajaxOptions.moduleName = "gridElement_kiss") ]

					= JSON.stringify( $.extend( {}, {

						pageVO: $.extend( {}, {
							curPage: +pagingInfo.pageNum - 1,
							incrementalPaging: false,
							pageSize: +pagingInfo.pageSize,
							totalRows: -1
						}, args.pageVO || {} )
					}, args.result || {} ) );

				/** Keep one ajax instance */
				request && request.abort();

				request = $.ajax( {

					type: "POST",

					beforeSend: function() {
						/** Show the loading */
						loading.fadeIn();
					},

					data: {
						name: ajaxOptions.serviceName,
						params: JSON.stringify( $.extend( {}, VO, ajaxOptions.params || {}, args.params || {} ) )
					}
				} )

				.done( function( data ) {

					try {
						data = eval( "(" + data + ")" );
						data = data[ "result" ][ ajaxOptions.moduleName ];
						data = JSON.parse( data );

					} catch ( ex ) {

						throw "Call the service '" + ajaxOptions.serviceName + "' failed:\n" + ex;
					}

					dataView.setItems( data.result.concat( $G.getAddRows ? $G.getAddRows() : [] ) );

					/** Render all rows */
					$G.invalidate();
					$G.resizeCanvas();

					with ( data.pageVO ) {
						callback( { pageNum: curPage - 1, pageSize: pageSize, totalPages: totalPages } );
					}
				} )

				.always( function() {
					loading.removeData( "data-request" ).fadeOut( 100 );
				} );

				loading.data( "data-request", request );

				return request;
			};

			/** Registe event handler */
			$G.onSort.subscribe( handleSort );
		} else {

			$G.onSort.unsubscribe( handleSort );
		}

		return pager;
	};
} );

<commit_msg>Update Remote.js<commit_after>
define( function() {

	var handleSort = function( e, args ) {

		if ( this.isGenius && this.isGenius() ) {

			var field = args.sortCol.field

			, dataView = this.getData();

			dataView.beginUpdate();

			dataView.sort( function( a, b ) {
				var x = a[ field ], y = b[ field ];

				return (x === y ? 0 : (x > y ? 1 : -1));
			}, args.sortAsc );

			dataView.endUpdate();
		}
		else this.getData().onPagingInfoChanged.notify( { doSearch: 1 } );
	};

	return function( $G, ajaxOptions, enable ) {

		/** All operations in server-side */
		var pager;

		if ( true === enable ) {

			var dataView, request, loading;

			if ( !ajaxOptions.serviceName ) {

				throw "Service name cann't be null";
			}

			dataView = $G.getData();
			loading = $( "

" );

			$( $G.getContainerNode() ).append( loading );

			pager = function( pagingInfo, callback, args ) {

				var VO = { wpf_dup_token: +new Date() + Math.random() }

				, request = loading.data( "data-request" );

				args = args || {};

				VO[ ajaxOptions.moduleName || (ajaxOptions.moduleName = "gridElement_kiss") ]

					= JSON.stringify( $.extend( {}, {

						pageVO: $.extend( {}, {
							curPage: +pagingInfo.pageNum - 1,
							incrementalPaging: false,
							pageSize: +pagingInfo.pageSize,
							totalRows: -1
						}, args.pageVO || {} )
					}, args.result || {} ) );

				/** Keep one ajax instance */
				request && request.abort();

				request = $.ajax( {

					type: "POST",

					beforeSend: function() {
						/** Show the loading */
						loading.fadeIn();
					},

					data: {
						name: ajaxOptions.serviceName,
						params: JSON.stringify( $.extend( {}, VO, ajaxOptions.params || {}, args.params || {} ) )
					}
				} )

				.done( function( data ) {

					try {
						data = eval( "(" + data + ")" );
						data = data[ "result" ][ ajaxOptions.moduleName ];
						data = JSON.parse( data );

					} catch ( ex ) {

						throw "Call the service '" + ajaxOptions.serviceName + "' failed:\n" + ex;
					}

					dataView.setItems( data.result.concat( $G.getAddRows ? $G.getAddRows() : [] ) );

					/** Render all rows */
					$G.invalidate();
					$G.resizeCanvas();

					with ( data.pageVO ) {
						callback( { pageNum: curPage - 1, pageSize: pageSize, totalPages: totalPages } );
					}
				} )

				.always( function() {
					loading.is( ":visible" ) ? loading.fadeOut( 100 ) : loading.css( "display", "none" );
					loading.removeData( "data-request" );
				} );

				loading.data( "data-request", request );

				return request;
			};

			/** Registe event handler */
			$G.onSort.subscribe( handleSort );
		} else {

			$G.onSort.unsubscribe( handleSort );
		}

		return pager;
	};
} );
<|endoftext|>


<commit_before>!( function( factory ) {
  if ( typeof define === "function" && define.amd ) {
    define( factory );
  } else {
    factory( );
  }
}(function(){

  window.customElements = window.customElements || {};

  var debug                 = false
    , namespace             = '_'
    , webComponentsCount    = 0
    , customElementsLoaded  = {}
    , webcomInited          = false
    , running               = false;

  // Override setAttribute function.
  var setAttributeFun = function(name, value) {

    var currentRole = this.getAttribute('data-is');
    if (name === 'data-bind') {

      this._dataBind = value;

      // Hide data-bind
      if (this.getAttribute('data-bind-show') === 'true') {
        Element.prototype.setAttribute.call(this, name, value);
      } else {
        Element.prototype.setAttribute.call(this, name, 'data-was-hidden-by-webcom');
      }

      // run template
      try {

        this.classList.add(currentRole);

        var tmpl = customElements[currentRole].querySelector('[data-role="template"]');
        if (!tmpl) {
          console.warn('Attribute named data-bind is not affected as template is null.')
          return;
        }
        tmpl[namespace + 'holder'] = this;
        tmpl[namespace + 'updateBy'](JSON.parse(value));

        ;(typeof this[namespace + 'updated'] === 'function') && this[namespace + 'updated']();

        // Execute template-updated script.

        // export current var to global
        var tempName = '_webcom' + Date.now() + (Math.random() + '').substr(2, 6);
        window[tempName] = this;

        var scripts = customElements[currentRole].querySelectorAll('script[data-run="template-updated"]');
        for (var j = 0; j < scripts.length; j++) {

          var s = document.createElement('script');
          s.setAttribute('data-runner', 'this-script-executed-by-webcom');
          s.setAttribute('data-run', 'template-updated-by-webcom');

          // Execute it with current this.
          s.innerHTML = '(function(webcom){' + scripts[j].innerHTML + '}).bind(window["' + tempName + '"])(window["' + tempName + '"]); //# sourceURL=' + currentRole + '-template-updated.js'

          // execute it.
          this.appendChild(s);

        }

        // Clear var.
        setTimeout(function(){

          delete window[tempName];

          // The appended child (html) may include customElement, so, you should reload it again.
          debug && console.log('dispatchEvent: webcom-reload.')
          document.dispatchEvent(new Event('webcom-reload'));

        }, 1000)

      } catch (e) {
        console.warn("Webcom can't update template successfully. Please check the data-bind value.")
        console.log(e)
      }

    } else {
      Element.prototype.setAttribute.call(this, name, value);
    }
  }

  /*
  *  Init custom Element
  *  1: add to custom element to document.body so that style & links can be effective.
  *  1: init template
  *  2: run scripts (except "template-updated" scripts)
  */
  var initCustomElement = function(role) {

    debug && console.log('init customElement :' + role);

    // Inited flag.
    if (customElementsLoaded[role]) return;
    customElementsLoaded[role] = true;

    var customElement = customElements[role];

    if (!customElement) {
      console.log('customElement ' + role + ' not Found.')
      return;
    }
    document.body.appendChild(customElement);
    customElement.style.display = 'none';

    // Init web component's template.
    var tmpls = customElement.querySelectorAll('[data-role="template"]');
    for (var i = tmpls.length - 1; i >= 0; i--)
      tmpls[i].dispatchEvent(new Event('template-reload-it', {bubbles: true}));

    // Execute script
    var scripts = customElement.querySelectorAll('script');
    for (var i = 0; i < scripts.length; i++) {

      if (scripts[i].type === '' || scripts[i].type === 'text/javascript') {

        // Skip the script only executed after template is updated.
        if (scripts[i].dataset.run) {
          continue;
        }

        // Execute the normal script.
        var s = document.createElement('script');
        s.setAttribute('data-runner', 'this-script-executed-by-webcom')
        scripts[i].src ? (s.src = scripts[i].src) : (s.innerHTML = scripts[i].innerHTML);
        scripts[i].remove();
        customElement.appendChild(s);

      }
    }
  }

  /*
  *  After loading all links. Init all web components in current document.
  *  1: init custom element if necessary.
  *  2: override current element's setAttribute function
  *  3: override current element's getAttribute function
  */
  var initWebComponents = function() {

    webComponentsCount--;
    if (webComponentsCount > 0) return;

    debug && console.log('initWebComponents...');

    var eles = document.querySelectorAll('[data-is]');
    for (var i = 0; i < eles.length; i++) {

      // Inited flag.
      if (eles[i][namespace + 'inited']) continue;
      eles[i][namespace + 'inited'] = true;

      eles[i]._innerHTML = eles[i].innerHTML || "";

      // Get its role.
      var role = eles[i].getAttribute('data-is');
      customElementsLoaded[role] ? 1: initCustomElement(role);

      // Bind function for template. Override setAttribute function.
      eles[i].setAttribute = setAttributeFun.bind(eles[i]);
      if (eles[i].getAttribute('data-bind')) {
        eles[i].setAttribute('data-bind', eles[i].getAttribute('data-bind'));
      }

      // Override getAttribute function.
      eles[i].getAttribute = function(name) {

        if (name === 'data-bind') {
          return this._dataBind;
        }
        return Element.prototype.getAttribute.call(this, name);

      }

      // Execute _loaded function.
      ;(typeof eles[i][namespace + 'loaded'] === 'function') && eles[i][namespace + 'loaded']();

      // Clone html to instance except CSS & JS.
      if (!customElements[role]) {
        console.log('customElement ' + role + ' not Found.')
        return;
      }

      var children = customElements[role].children;
      for (var j = 0; j < children.length; j++) {

        if (children[j].nodeName !== 'SCRIPT' && children[j].nodeName !== 'STYLE' && children[j].nodeName !== 'LINK')
          eles[i].appendChild(children[j].cloneNode(true))

      }

    }

    // Trigger webcom-inited event on document.
    running = false;
    ;(webcomInited === false) && document.dispatchEvent(new Event('webcom-inited')) && (webcomInited = true);

  }

  var loadFinished = function(link, text) {

    try {

      var doc = document.createDocumentFragment();
      var wrapper = document.createElement("div");
      wrapper.innerHTML = text;
      doc.appendChild(wrapper);
      link[namespace + 'doc'] = doc;

      // Mount all "data-register" element to customElements.
      var eles = doc.querySelectorAll('[data-register]');
      for (var j = 0; j < eles.length; j++) {

        var register = eles[j].getAttribute('data-register');
        customElements[register] = eles[j];

      }

    } catch (e) {
      console.log(e)
    }

    initWebComponents();

  }

  var loadLink = function(link) {

    var url = link.href;

    // No extern html should be loaded.
    if (!url) {
      initWebComponents();
      return;
    }

    var xmlHttp = new XMLHttpRequest()
    xmlHttp.open("GET", url, true);
    xmlHttp.onreadystatechange = function(){

      // Extern html is loaded
      if (xmlHttp.readyState === 4) {

        if (xmlHttp.status !== 200) {

          console.log('request error');
          initWebComponents();

        } else {

          if (link.dataset.cacheid) {
            var obj = {
              expires: (+link.dataset.cachetime || 8.64e7) + Date.now(), // DefaultTime 1 day = 8.64e7 = 1000 * 60 * 60 * 24 = 86400000
              text: xmlHttp.responseText
            }
            localStorage.setItem("webcoms-" + link.dataset.cacheid, JSON.stringify(obj));
          }

          loadFinished(link, xmlHttp.responseText);
        }

      }
    }

    var cache = localStorage.getItem('webcoms-' + link.dataset.cacheid);
    try {
      cache = JSON.parse(cache);
    } catch (e) {
      cache = null;
    }

    if (cache) {
      if (cache.expires >= Date.now()) {
        loadFinished(link, cache.text);
      } else {
        localStorage.removeItem('webcoms-' + link.dataset.cacheid);
        xmlHttp.send(null);
      }
    } else {
      xmlHttp.send(null);
    }
  }

  /*
  *  load all 'import-webcom' links
  */
  var init = function() {

    if (running) return;
    running = true;

    var links     = document.querySelectorAll('link[rel="import-webcom"]'),
        link;

    webComponentsCount = links.length;

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

      // Skip inited link.
      if (link[namespace + 'inited']) {
        initWebComponents();
        continue;
      }
      link[namespace + 'inited'] = true;

      // load html & init it.
      loadLink(link);
    }
  }

  if (document.readyState === 'complete' || document.readyState === 'interactive') {
    init()
  } else {
    document.addEventListener('readystatechange', function(e) {
      if (document.readyState === 'interactive') {
        init();
      }
    })
  }

  document.addEventListener('webcom-reload', init);
  document.addEventListener('reload', init)

  return null;
}));
<commit_msg>initWebComponentsFinished should not execute load function.<commit_after>!( function( factory ) {
  if ( typeof define === "function" && define.amd ) {
    define( factory );
  } else {
    factory( );
  }
}(function(){

  window.customElements = window.customElements || {};

  var debug                 = false
    , namespace             = '_'
    , webComponentLinks     = 0
    , webComponentsCount    = 0
    , customElementsLoaded  = {}
    , webcomInited          = false
    , running               = false;

  // Override setAttribute function.
  var setAttributeFun = function(name, value) {

    var currentRole = this.getAttribute('data-is')
      , self = this;

    if (name === 'data-bind') {

      this._dataBind = value;

      // Hide data-bind
      if (this.getAttribute('data-bind-show') === 'true') {
        Element.prototype.setAttribute.call(this, name, value);
      } else {
        Element.prototype.setAttribute.call(this, name, 'data-was-hidden-by-webcom');
      }

      // run template
      try {

        this.classList.add(currentRole);

        var tmpl = customElements[currentRole].querySelector('[data-role="template"]');
        if (!tmpl) {
          console.warn('Attribute named data-bind is not affected as template is null.')
          return;
        }
        tmpl[namespace + 'holder'] = this;
        tmpl[namespace + 'updateBy'](JSON.parse(value));

        ;(typeof this[namespace + 'updated'] === 'function') && this[namespace + 'updated']();

        // Execute template-updated script.

        // export current var to global
        var tempName = '_webcom' + Date.now() + (Math.random() + '').substr(2, 6);
        window[tempName] = this;

        var scripts = customElements[currentRole].querySelectorAll('script[data-run="template-updated"]');
        for (var j = 0; j < scripts.length; j++) {

          var s = document.createElement('script');
          s.setAttribute('data-runner', 'this-script-executed-by-webcom');
          s.setAttribute('data-run', 'template-updated-by-webcom');

          // Execute it with current this.
          s.innerHTML = '(function(webcom){' + scripts[j].innerHTML + '}).bind(window["' + tempName + '"])(window["' + tempName + '"]); //# sourceURL=' + currentRole + '-template-updated.js'

          // execute it.
          this.appendChild(s);

        }

        // Clear var.
        setTimeout(function(){

          delete window[tempName];

          // The appended child (html) may include customElement, so, you should reload it again.
          if (self.querySelector('[data-is]')) {
            debug && console.log('dispatchEvent: webcom-reload.')
            document.dispatchEvent(new Event('webcom-reload'));
          }

        }, 1000)

      } catch (e) {
        console.warn("Webcom can't update template successfully. Please check the data-bind value.")
        console.log(e)
      }

    } else {
      Element.prototype.setAttribute.call(this, name, value);
    }
  }

  /*
  *  Init custom Element
  *  1: add to custom element to document.body so that style & links can be effective.
  *  1: init template
  *  2: run scripts (except "template-updated" scripts)
  */
  var initCustomElement = function(ele, callback) {

    var role = ele.dataset.is;

    if (customElementsLoaded[role]) {
      return callback(ele);
    }

    debug && console.log('init customElement :' + role);

    // Inited flag.
    if (customElementsLoaded[role]) return;
    customElementsLoaded[role] = true;

    var customElement   = customElements[role]
      , tmplsInitCount  = 0
      , tmpls
      , scripts;

    if (!customElement) {

      console.log('customElement ' + role + ' not Found.')
      return callback();
    }

    tmpls   = customElement.querySelectorAll('[data-role="template"]')
    scripts = customElement.querySelectorAll('script');

    document.body.appendChild(customElement);
    customElement.style.display = 'none';

    // Execute script
    var executeScript = function() {

      for (var i = 0; i < scripts.length; i++) {

        if (scripts[i].type === '' || scripts[i].type === 'text/javascript') {

          // Skip the script only executed after template is updated.
          if (scripts[i].dataset.run) {
            continue;
          }

          // Execute the normal script.
          var s = document.createElement('script');
          s.setAttribute('data-runner', 'this-script-executed-by-webcom')
          scripts[i].src ? (s.src = scripts[i].src) : (s.innerHTML = scripts[i].innerHTML);
          scripts[i].remove();
          customElement.appendChild(s);

        }
      }
      return callback(ele);
    }

    // Init web component's templates.
    for (var i = 0; i < tmpls.length; i++) {

      // After all templates inited;
      tmpls[i].addEventListener('template-inited', function() {
        tmplsInitCount++;
        if (tmplsInitCount === tmpls.length) {
          executeScript();
        }
      })

      tmpls[i].dispatchEvent(new Event('template-reload-it', {bubbles: true}));
    }

  }

  var initWebComponentsFinished = function(ele) {

    // All webComponents is loaded
    if (webComponentsCount <= 0) {

      // Trigger webcom-inited event on document.
      running = false;
      ;(webcomInited === false) && document.dispatchEvent(new Event('webcom-inited')) && (webcomInited = true);

    }
  }

  /*
  *  After loading all links. Init all web components in current document.
  *  1: init custom element if necessary.
  *  2: override current element's setAttribute function
  *  3: override current element's getAttribute function
  */
  var initWebComponents = function() {

    webComponentLinks--;
    if (webComponentLinks > 0) return;

    var eles = document.querySelectorAll('[data-is]');

    webComponentsCount = eles.length;

    debug && console.log('initWebComponents... count:' + webComponentsCount);
    for (var i = 0; i < eles.length; i++) {

      // Inited flag.
      if (eles[i][namespace + 'inited']) {
        webComponentsCount--;
        initWebComponentsFinished(eles[i]);
        continue;
      }

      // init flag && save old html
      eles[i][namespace + 'inited'] = true;
      eles[i]._innerHTML = eles[i].innerHTML || "";

      // Current web component is not exist.
      if (!customElements[eles[i].dataset.is]) {

        console.log('customElement ' + eles[i].dataset.is + ' not Found.')

      // Clone html to instance except CSS & JS.
      } else {

        var children = customElements[eles[i].dataset.is].children;
        for (var j = 0; j < children.length; j++) {

          if (children[j].nodeName !== 'SCRIPT' && children[j].nodeName !== 'STYLE' && children[j].nodeName !== 'LINK') {
            eles[i].appendChild(children[j].cloneNode(true))
          }

        }
      }

      // Init current customElement
      debug && console.log('initCustomElement ' + eles[i].dataset.is);
      initCustomElement(eles[i], function(ele) {

        webComponentsCount--;
        debug && console.log('webComponentsCount:' + webComponentsCount)

        // Bind function for template. Override setAttribute function.
        ele.setAttribute = setAttributeFun.bind(ele);
        if (ele.getAttribute('data-bind')) {
          ele.setAttribute('data-bind', ele.getAttribute('data-bind'));
        }

        // Override getAttribute function.
        ele.getAttribute = function(name) {

          if (name === 'data-bind') {
            return this._dataBind;
          }
          return Element.prototype.getAttribute.call(this, name);

        }

        // Execute _loaded function.
        ;(typeof ele[namespace + 'loaded'] === 'function') && ele[namespace + 'loaded']();

        initWebComponentsFinished(ele);

      });

    }

  }

  var loadLinkFinished = function(link, text) {

    try {

      var doc = document.createDocumentFragment();
      var wrapper = document.createElement("div");
      wrapper.innerHTML = text;
      doc.appendChild(wrapper);
      link[namespace + 'doc'] = doc;

      // Mount all "data-register" element to customElements.
      var eles = doc.querySelectorAll('[data-register]');
      for (var j = 0; j < eles.length; j++) {

        var register = eles[j].getAttribute('data-register');
        customElements[register] = eles[j];

      }

    } catch (e) {
      console.log(e)
    }

    initWebComponents();

  }

  var loadSingleLink = function(link) {

    var url = link.href;

    // No extern html should be loaded.
    if (!url) {
      initWebComponents();
      return;
    }

    var xmlHttp = new XMLHttpRequest()
    xmlHttp.open("GET", url, true);
    xmlHttp.onreadystatechange = function(){

      // Extern html is loaded
      if (xmlHttp.readyState === 4) {

        if (xmlHttp.status !== 200) {

          console.log('request error');
          initWebComponents();

        } else {

          if (link.dataset.cacheid) {
            var obj = {
              expires: (+link.dataset.cachetime || 8.64e7) + Date.now(), // DefaultTime 1 day = 8.64e7 = 1000 * 60 * 60 * 24 = 86400000
              text: xmlHttp.responseText
            }
            localStorage.setItem("webcoms-" + link.dataset.cacheid, JSON.stringify(obj));
          }

          loadLinkFinished(link, xmlHttp.responseText);
        }

      }
    }

    /* Get cache from localStorage. */
    var cache = localStorage.getItem('webcoms-' + link.dataset.cacheid);
    try {
      cache = JSON.parse(cache);
    } catch (e) {
      cache = null;
    }

    /* Cache is exitst*/
    if (cache) {

      if (cache.expires >= Date.now()) {
        loadLinkFinished(link, cache.text);

      /*
       Cache is out of date.
        1, remove cache
        2, send new request.
      */
      } else {
        localStorage.removeItem('webcoms-' + link.dataset.cacheid);
        xmlHttp.send(null);
      }
    } else {
      xmlHttp.send(null);
    }
  }

  /*
  *  load all 'import-webcom' links
  */
  var init = function() {

    if (running) return;
    running = true;

    var links     = document.querySelectorAll('link[rel="import-webcom"]'),
        link;

    webComponentLinks = links.length;

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

      // Skip inited link.
      if (link[namespace + 'inited']) {
        initWebComponents();
        continue;
      }
      link[namespace + 'inited'] = true;

      // load html & init it.
      loadSingleLink(link);
    }
  }

  if (document.readyState === 'complete' || document.readyState === 'interactive') {
    init()
  } else {
    document.addEventListener('readystatechange', function(e) {
      if (document.readyState === 'interactive') {
        init();
      }
    })
  }

  document.addEventListener('webcom-reload', init);
  document.addEventListener('reload', init);

  document.addEventListener('template-appended', init);
  document.addEventListener('template-updated', init);
  return null;
}));
<|endoftext|>


<commit_before>define([
    'require',
    '../logging',
    '../patterns'
], function(require) {
    var log = require('../logging').getLogger('edit');

    // create a div after the textarea
    // copy the textarea's content unquoted to the div
    // hide the textarea
    // copy content back before form is serialized
    var text2div = function($el) {
        // hide textarea
        $el.hide();

        // make sure textarea has an id
        // XXX: generate something proper
        var id = $el.attr('id');
        if (!id) {
            id = "my-id-that-should-be-replaced-by-something-unique";
            $el.attr({id: id});
        }

        // create contenteditable div
        var editid = 'edit-' + id,
            $edit = $('
').insertAfter($el);
        $edit.html($el.val());
        $edit.attr({style: 'min-height: 50px'});

        // ensure form is ajaxified and copy content back before serialize
        var ajaxify = require('../patterns').ajaxify.init,
            $form    = $el.parents('form');
        ajaxify($form);
        $form.on('form-pre-serialize', function() {
            $el.html($edit.html());
        });

        return $edit;
    };

    var init = function($el, opts) {
        var $edit = text2div($el);

        var $ctrls   = $('.editor-controls'),
            buttons  = {};

        buttons.bold                = $ctrls.find('.strong');
        buttons.italic              = $ctrls.find('.emphasised');
        buttons.insertorderedlist   = $ctrls.find('.list-ordered');
        buttons.insertunorderedlist = $ctrls.find('.list-unordered');
        //buttons.insertparagraph     = $ctrls.find('.paragraph');
        buttons.clear               = $ctrls.find('.clear');
        buttons.inserth1            = $ctrls.find('.header_1');
        buttons.inserth2            = $ctrls.find('.header_2');
        buttons.inserth3            = $ctrls.find('.header_3');
        buttons.upload_image        = $ctrls.find('.upload_image');
        buttons.link_image          = $ctrls.find('.link_image');

        var button_handler = {
            'bold'                  : function(){ document.execCommand('bold'); },
            'italic'                : function(){ document.execCommand('italic'); },
            'insertparagraph'       : function(){ return 0; },
            'insertorderedlist'     : function(){ document.execCommand('insertorderedlist'); },
            'insertunorderedlist'   : function(){ document.execCommand('insertunorderedlist'); },
            'inserth1'              : function(){ wrap_selection('h1') },
            'inserth2'              : function(){ wrap_selection('h2') },
            'inserth3'              : function(){ wrap_selection('h3') },
            'clear'                 : function(){
                var selection_node = $(window.getSelection().anchorNode);
                document.execCommand('removeformat');
                if (is_contenteditable(selection_node)) {
                    selection_node.unwrap();
                    if (!$(selection_node).parent().is("p")) {
                        $(selection_node).wrap('
');
                    }
                }
            },
            'upload_image'          : function(){ document.execCommand(); },
            'link_image'            : function(){
                var source = prompt('URL of Image');
                if(source) {
                    document.execCommand('insertImage', false, source);
                }
            }
        };

        // utility method for determining if something is contenteditable
        var is_contenteditable = function(element) {
            mapped_elements = $(element).parents().map(function() {
                if ($(this).is("[contenteditable='true']")) {
                    return true;
                }
            }).get();

            // mapped_element[0] is the closest parent
            // in the document tree -- If it's true
            // the element will inherit contentEditable
            if (mapped_elements[0]) { return true; }
            return false;
        };

        // simply replaces
        // rather than toggles, but
        // theres no reason you couldn't have
        // it do both!
        var wrap_selection = function(wrap_html){
            var selection_node = $(window.getSelection().anchorNode);
            if(is_contenteditable(selection_node)) {
                // You just want to unwrap if your
                // parent is already selected
                if(selection_node.parent().is(wrap_html)) {
                    selection_node.unwrap();
                }
                else {
                    selection_node.wrap("<" + wrap_html + ">");
                }
            }
            // wrap() normally breaks contentEditable
            // this is a hacky replacement
            selection_node.attr('contenteditable', true);
        };

        // lame helper that
        // toggles the class,
        // triggers the handler
        var button_click = function(element) {
            buttons[element].toggleClass('selected');
            button_handler[element]();
        };

        // bind click to button_click()/1
        for (var key in buttons) {
            buttons[key].click(function(element){
                return function() {
                    log.debug('clicked', element);
                    button_click(element);
                };
            }(key));
        }

        // Enables contentEditable
        $('form').attr('contenteditable','true');

        var setstate = function(selection) {
            var markup = selection.markupEffectiveAtStart;
            if (!markup) return;
            $ctrls.find('*').removeClass('selected');
            $.each(markup, function(idx, el) {
                var tag = el.nodeName.toLowerCase(),
                    $button = buttons[tag];
                if ($button) $button.addClass('selected');
                log.debug('selected', tag);
            });
        };
    };

    var pattern = {
        markup_trigger: 'form textarea.edit',
        initialised_class: 'edit',
        init: init
    };
    return pattern;
});
<commit_msg>simplify is_contenteditable<commit_after>define([
    'require',
    '../logging',
    '../patterns'
], function(require) {
    var log = require('../logging').getLogger('edit');

    // create a div after the textarea
    // copy the textarea's content unquoted to the div
    // hide the textarea
    // copy content back before form is serialized
    var text2div = function($el) {
        // hide textarea
        $el.hide();

        // make sure textarea has an id
        // XXX: generate something proper
        var id = $el.attr('id');
        if (!id) {
            id = "my-id-that-should-be-replaced-by-something-unique";
            $el.attr({id: id});
        }

        // create contenteditable div
        var editid = 'edit-' + id,
            $edit = $('
').insertAfter($el);
        $edit.html($el.val());
        $edit.attr({style: 'min-height: 50px'});

        // ensure form is ajaxified and copy content back before serialize
        var ajaxify = require('../patterns').ajaxify.init,
            $form    = $el.parents('form');
        ajaxify($form);
        $form.on('form-pre-serialize', function() {
            $el.html($edit.html());
        });

        return $edit;
    };

    var init = function($el, opts) {
        var $edit = text2div($el);

        var $ctrls   = $('.editor-controls'),
            buttons  = {};

        buttons.bold                = $ctrls.find('.strong');
        buttons.italic              = $ctrls.find('.emphasised');
        buttons.insertorderedlist   = $ctrls.find('.list-ordered');
        buttons.insertunorderedlist = $ctrls.find('.list-unordered');
        //buttons.insertparagraph     = $ctrls.find('.paragraph');
        buttons.clear               = $ctrls.find('.clear');
        buttons.inserth1            = $ctrls.find('.header_1');
        buttons.inserth2            = $ctrls.find('.header_2');
        buttons.inserth3            = $ctrls.find('.header_3');
        buttons.upload_image        = $ctrls.find('.upload_image');
        buttons.link_image          = $ctrls.find('.link_image');

        var button_handler = {
            'bold'                  : function(){ document.execCommand('bold'); },
            'italic'                : function(){ document.execCommand('italic'); },
            'insertparagraph'       : function(){ return 0; },
            'insertorderedlist'     : function(){ document.execCommand('insertorderedlist'); },
            'insertunorderedlist'   : function(){ document.execCommand('insertunorderedlist'); },
            'inserth1'              : function(){ wrap_selection('h1') },
            'inserth2'              : function(){ wrap_selection('h2') },
            'inserth3'              : function(){ wrap_selection('h3') },
            'clear'                 : function(){
                var selection_node = $(window.getSelection().anchorNode);
                document.execCommand('removeformat');
                if (is_contenteditable(selection_node)) {
                    selection_node.unwrap();
                    if (!$(selection_node).parent().is("p")) {
                        $(selection_node).wrap('
');
                    }
                }
            },
            'upload_image'          : function(){ document.execCommand(); },
            'link_image'            : function(){
                var source = prompt('URL of Image');
                if(source) {
                    document.execCommand('insertImage', false, source);
                }
            }
        };

        // utility method for determining if something is contenteditable
        var is_contenteditable = function(el) {
            return $(el).is("[contenteditable=true]") || (
                $(el).parents("[contenteditable=true]").length > 0
            );
        };

        // simply replaces
        // rather than toggles, but
        // theres no reason you couldn't have
        // it do both!
        var wrap_selection = function(wrap_html){
            var selection_node = $(window.getSelection().anchorNode);
            if(is_contenteditable(selection_node)) {
                // You just want to unwrap if your
                // parent is already selected
                if(selection_node.parent().is(wrap_html)) {
                    selection_node.unwrap();
                }
                else {
                    selection_node.wrap("<" + wrap_html + ">");
                }
            }
            // wrap() normally breaks contentEditable
            // this is a hacky replacement
            selection_node.attr('contenteditable', true);
        };

        // lame helper that
        // toggles the class,
        // triggers the handler
        var button_click = function(element) {
            buttons[element].toggleClass('selected');
            button_handler[element]();
        };

        // bind click to button_click()/1
        for (var key in buttons) {
            buttons[key].click(function(element){
                return function() {
                    log.debug('clicked', element);
                    button_click(element);
                };
            }(key));
        }

        // Enables contentEditable
        $('form').attr('contenteditable','true');

        var setstate = function(selection) {
            var markup = selection.markupEffectiveAtStart;
            if (!markup) return;
            $ctrls.find('*').removeClass('selected');
            $.each(markup, function(idx, el) {
                var tag = el.nodeName.toLowerCase(),
                    $button = buttons[tag];
                if ($button) $button.addClass('selected');
                log.debug('selected', tag);
            });
        };
    };

    var pattern = {
        markup_trigger: 'form textarea.edit',
        initialised_class: 'edit',
        init: init
    };
    return pattern;
});
<|endoftext|>


<commit_before>import buildfire from 'buildfire';
import {filter, find} from 'lodash';
import "./lib/markercluster.js";

import "../css/general.css";
import "../css/slider.css";
import "./filterControl.js";
import "./map.js";
import "./list.js";
import "./detail.js";
import "./router.js";
import PlacesSort from "./PlacesSort.js";

window.app = {
    goBack: null,
    settings: {
        viewStates: {map: 'map', list: 'list', detail: 'detail'},
        sortOptions: {alpha: 'alpha', alphaDesc: 'alphaDesc', manual: 'manual'},
        placesTag: 'places',
        cloudImg: {
            domain:'https://czi3m2qn.cloudimg.io',
            operations: {
                cdn: '/cdn/n/n',
                width: '/s/width',
                crop: '/s/crop'
            }
        }
    },
    views: {
        listView: document.getElementById('listView'),
        mapView: document.getElementById('mapView'),
        detailView: document.getElementById('detailView'),
        sideNav: document.getElementById('sideNav'),
    },
    state: {
        mapInitiated: false,
        mode: null,
        activeView: null,
        places: [],
        markers: [],
        bounds: null,
        filteredPlaces: [],
        selectedPlace: [],
        sortBy: null,
        categories: [],
        navHistory: [],
        isBackNav: false
    },
    backButtonInit: () => {
        window.app.goBack = window.buildfire.navigation.onBackButtonClick;

        buildfire.navigation.onBackButtonClick = function() {
            if (window.app.state.navHistory.length > 0) {

                //Remove the current state
                if(window.app.state.mode === window.app.state.navHistory[window.app.state.navHistory.length-1])
                    window.app.state.navHistory.pop();

                //Navigate to the previous state
                let lastNavState = window.app.state.navHistory[window.app.state.navHistory.length-1];

                window.app.state.isBackNav = true;

                window.router.navigate(lastNavState);
            }
            else{
                //If the plugin is set as the launcher plugin, there's no where to go back to
                //Also, keep track of current mode, since it was already popped off the history stack
                if(window.location.href.includes('launcherPlugin'))
                    window.app.state.navHistory.push(window.app.state.mode);
                else
                    window.app.goBack();
            }
        };
    },
    init: (placesCallback, positionCallback) => {
        window.buildfire.appearance.titlebar.show();

        window.app.backButtonInit();

        buildfire.datastore.get (window.app.settings.placesTag, function(err, results){
            if(err){
              console.error('datastore.get error', err);
              return;
            }

            let places,
                data = results.data;

            if(data && data.places){
              if(data.categories){
                window.app.state.categories = data.categories.map(category => {
                    return {name: category, isActive: true};
                });
              }

              window.app.state.mode = data.defaultView;

              let sortBy = window.PlacesSort[data.sortBy];
              places = data.places.sort(sortBy);

              window.app.state.places = places;
              window.app.state.filteredPlaces = places;
              window.app.state.sortBy = data.sortBy;
              window.app.state.defaultView = data.defaultView;
            }

            placesCallback(null, places);
        });

        console.log('Calling getCurrentPosition');

        buildfire.geo.getCurrentPosition({}, (err, position) => {
            console.log('getCurrentPosition result', err, position);
            if(err){
                console.error('getCurrentPosition', err);
                return;
            }

            if(position && position.coords){
                positionCallback(null, position.coords);
            }
        });

        buildfire.datastore.onUpdate(function(event) {
          if(event.tag === window.app.settings.placesTag){

              console.log('Got update');
              location.reload(); // TEMPORARY SOLUTION FOR THE DEMO

              let currentPlaces = window.app.state.places;
              let newPlaces = event.data.places;
              let currentSortOrder = window.app.state.sortBy;
              let newSortOrder = event.data.sortBy;
              let newViewState = event.data.defaultView;
              let currentDefaultView = window.app.state.defaultView;
              let newDefaultView = event.data.defaultView;

              /**
               * SORT ORDER
               */
              if(currentSortOrder != newSortOrder){
                  window.app.state.sortBy = newSortOrder;
                  let sortBy = PlacesSort[window.app.state.sortBy];
                  window.app.state.places.sort(sortBy);

                  if(window.app.state.mode === window.app.settings.viewStates.list)
                    window.loadList(window.app.state.places);

                  return;
              }

              let defaultViewChanged = currentDefaultView !== newDefaultView;
              let notInDefaultView = newDefaultView !== window.app.state.mode;

              // We want to update the widget to reflect the new default view if the setting
              // was changed and the user is not in that view already
              if (defaultViewChanged && notInDefaultView) {
                window.router.navigate(newViewState);
                window.app.state.mode = newViewState;
                return;
              }

              //Do comparison to see what's changed
              let updatedPlaces = filter(newPlaces, (newPlace) => { return !find(currentPlaces, newPlace)});

              if(window.app.state.mode === window.app.settings.viewStates.map){
                  window.mapView.updateMap(updatedPlaces);
              }else{
                  //Load new items
                  window.listView.updateList(updatedPlaces);
              }
          }
        });
    },
    gotPieceOfData() {
      if (window.app.state.places && window.app.state.location) {
        let { location } = window.app.state;
        let destinations = [];

        window.app.state.places.forEach(place => {
          destinations.push(new window.google.maps.LatLng(place.address.lat, place.address.lng));
        });

        let origin = [{lat: location.latitude, lng: location.longitude}];

        let service = new window.google.maps.DistanceMatrixService();

        service.getDistanceMatrix({
            origins: origin,
            destinations: destinations,
            travelMode: window.google.maps.TravelMode.DRIVING,
            unitSystem: window.google.maps.UnitSystem.IMPERIAL //google.maps.UnitSystem.METRIC
        }, (response) => {
            //Update places with distance
            window.app.state.places.map((place, index)=>{
                if(response.rows[0].elements[index].distance)
                    place.distance = response.rows[0].elements[index].distance.text;
                    place.distanceInMeters = response.rows[0].elements[index].distance.value;
            });

            window.listView.updateDistances(window.app.state.filteredPlaces);
        });
      }
    },
    gotPlaces(err, places) {
        if(window.app.state.mode === window.app.settings.viewStates.list){
            window.initList(places, true);
            //We can not pre-init the map, as it needs to be visible
        }
        else{
            window.initMap(places, true);
            window.initList(places);
        }
        window.app.gotPieceOfData();
    },

    gotLocation(err, location) {
        window.app.state.location = location;
        window.app.gotPieceOfData();
    }
};

//document.addEventListener('DOMContentLoaded', () => window.app.init( window.app.gotPlaces, window.app.gotLocation));
app.init(app.gotPlaces, app.gotLocation);
window.initRouter();
<commit_msg>Addressed edge case bug when plugin is launcher<commit_after>import buildfire from 'buildfire';
import {filter, find} from 'lodash';
import "./lib/markercluster.js";

import "../css/general.css";
import "../css/slider.css";
import "./filterControl.js";
import "./map.js";
import "./list.js";
import "./detail.js";
import "./router.js";
import PlacesSort from "./PlacesSort.js";

window.app = {
    goBack: null,
    settings: {
        viewStates: {map: 'map', list: 'list', detail: 'detail'},
        sortOptions: {alpha: 'alpha', alphaDesc: 'alphaDesc', manual: 'manual'},
        placesTag: 'places',
        cloudImg: {
            domain:'https://czi3m2qn.cloudimg.io',
            operations: {
                cdn: '/cdn/n/n',
                width: '/s/width',
                crop: '/s/crop'
            }
        }
    },
    views: {
        listView: document.getElementById('listView'),
        mapView: document.getElementById('mapView'),
        detailView: document.getElementById('detailView'),
        sideNav: document.getElementById('sideNav'),
    },
    state: {
        mapInitiated: false,
        mode: null,
        activeView: null,
        places: [],
        markers: [],
        bounds: null,
        filteredPlaces: [],
        selectedPlace: [],
        sortBy: null,
        categories: [],
        navHistory: [],
        isBackNav: false
    },
    backButtonInit: () => {
        window.app.goBack = window.buildfire.navigation.onBackButtonClick;

        buildfire.navigation.onBackButtonClick = function() {
            const isLauncher = window.location.href.includes('launcherPlugin');

            if (window.app.state.navHistory.length > 0) {

                //Remove the current state
                if(window.app.state.mode === window.app.state.navHistory[window.app.state.navHistory.length-1]){

                    //Don't remove last state, if launcher plugin
                    if(!isLauncher || window.app.state.navHistory.length != 1){
                        window.app.state.navHistory.pop();
                    }
                }

                //Navigate to the previous state
                let lastNavState = window.app.state.navHistory[window.app.state.navHistory.length-1];

                window.app.state.isBackNav = true;

                window.router.navigate(lastNavState);
            }
            else{
                window.app.goBack();
            }
        };
    },
    init: (placesCallback, positionCallback) => {
        window.buildfire.appearance.titlebar.show();

        window.app.backButtonInit();

        buildfire.datastore.get (window.app.settings.placesTag, function(err, results){
            if(err){
              console.error('datastore.get error', err);
              return;
            }

            let places,
                data = results.data;

            if(data && data.places){
              if(data.categories){
                window.app.state.categories = data.categories.map(category => {
                    return {name: category, isActive: true};
                });
              }

              window.app.state.mode = data.defaultView;

              let sortBy = window.PlacesSort[data.sortBy];
              places = data.places.sort(sortBy);

              window.app.state.places = places;
              window.app.state.filteredPlaces = places;
              window.app.state.sortBy = data.sortBy;
              window.app.state.defaultView = data.defaultView;
            }

            placesCallback(null, places);
        });

        console.log('Calling getCurrentPosition');

        buildfire.geo.getCurrentPosition({}, (err, position) => {
            console.log('getCurrentPosition result', err, position);
            if(err){
                console.error('getCurrentPosition', err);
                return;
            }

            if(position && position.coords){
                positionCallback(null, position.coords);
            }
        });

        buildfire.datastore.onUpdate(function(event) {
          if(event.tag === window.app.settings.placesTag){

              console.log('Got update');
              location.reload(); // TEMPORARY SOLUTION FOR THE DEMO

              let currentPlaces = window.app.state.places;
              let newPlaces = event.data.places;
              let currentSortOrder = window.app.state.sortBy;
              let newSortOrder = event.data.sortBy;
              let newViewState = event.data.defaultView;
              let currentDefaultView = window.app.state.defaultView;
              let newDefaultView = event.data.defaultView;

              /**
               * SORT ORDER
               */
              if(currentSortOrder != newSortOrder){
                  window.app.state.sortBy = newSortOrder;
                  let sortBy = PlacesSort[window.app.state.sortBy];
                  window.app.state.places.sort(sortBy);

                  if(window.app.state.mode === window.app.settings.viewStates.list)
                    window.loadList(window.app.state.places);

                  return;
              }

              let defaultViewChanged = currentDefaultView !== newDefaultView;
              let notInDefaultView = newDefaultView !== window.app.state.mode;

              // We want to update the widget to reflect the new default view if the setting
              // was changed and the user is not in that view already
              if (defaultViewChanged && notInDefaultView) {
                window.router.navigate(newViewState);
                window.app.state.mode = newViewState;
                return;
              }

              //Do comparison to see what's changed
              let updatedPlaces = filter(newPlaces, (newPlace) => { return !find(currentPlaces, newPlace)});

              if(window.app.state.mode === window.app.settings.viewStates.map){
                  window.mapView.updateMap(updatedPlaces);
              }else{
                  //Load new items
                  window.listView.updateList(updatedPlaces);
              }
          }
        });
    },
    gotPieceOfData() {
      if (window.app.state.places && window.app.state.location) {
        let { location } = window.app.state;
        let destinations = [];

        window.app.state.places.forEach(place => {
          destinations.push(new window.google.maps.LatLng(place.address.lat, place.address.lng));
        });

        let origin = [{lat: location.latitude, lng: location.longitude}];

        let service = new window.google.maps.DistanceMatrixService();

        service.getDistanceMatrix({
            origins: origin,
            destinations: destinations,
            travelMode: window.google.maps.TravelMode.DRIVING,
            unitSystem: window.google.maps.UnitSystem.IMPERIAL //google.maps.UnitSystem.METRIC
        }, (response) => {
            //Update places with distance
            window.app.state.places.map((place, index)=>{
                if(response.rows[0].elements[index].distance)
                    place.distance = response.rows[0].elements[index].distance.text;
                    place.distanceInMeters = response.rows[0].elements[index].distance.value;
            });

            window.listView.updateDistances(window.app.state.filteredPlaces);
        });
      }
    },
    gotPlaces(err, places) {
        if(window.app.state.mode === window.app.settings.viewStates.list){
            window.initList(places, true);
            //We can not pre-init the map, as it needs to be visible
        }
        else{
            window.initMap(places, true);
            window.initList(places);
        }
        window.app.gotPieceOfData();
    },

    gotLocation(err, location) {
        window.app.state.location = location;
        window.app.gotPieceOfData();
    }
};

//document.addEventListener('DOMContentLoaded', () => window.app.init( window.app.gotPlaces, window.app.gotLocation));
app.init(app.gotPlaces, app.gotLocation);
window.initRouter();
<|endoftext|>