<commit_before>var util = require('util');
var EventEmitter = require('events').EventEmitter;
var mdheadline = require('mdheadline');
var lines = require('./lines');

var match = function (re, text, fn) {
	var m;
	var cnt = 0;
	re.lastIndex = 0;
	m = re.exec(text);
	if (re.global) {
		while(m) {
			cnt = cnt + 1;
			fn(m);
			m = re.exec(text);
		}
	} else if (m) {
		cnt = cnt + 1;
		fn(m);
	}
	return cnt;
};

var MdParser = function (input, encoding) {

	var headlinePattern = /^(#+)\s+(.*?)\s*$/;
	var headline1Pattern = /^==+\s*$/;
	var headline2Pattern = /^--+\s*$/;

	var internalLinkPattern = /(?:^|[^\]\)!])\[([^\]]+)\](?:\[([^\]]*)\])?/g;

	var externalLinkPattern = /(?:^|[^\]!])\[([^\]]+)\]\(([^\)]+)\)/g;
	var urlLinkPattern = /<([^>\s]+)>/g;

	var codePattern = /^(?: {4}|\t)(.*)$/;
	var fencedCodeStartPattern = /^(`{3,}|~{3,})\s*(.*?)\s*$/;

	var commentPattern = /<!--(.*?)-->/g;

	var commentStartPattern = /(?:<!--)(?!.*<!--)(.*?)$/;
	var commentEndPattern = /^(.*?)-->/;

	var citationPattern = /^ {0,3}(>[\s>]*)(.*)\s*$/;

	var that = this;

	var s = lines(input, encoding);
	s.pause();
	if (s === undefined) {
		throw 'Invalid input.';
	}
	s.setEncoding('utf8');
	that._inputStream = s;

	var row = 0;
	var lastLine = null;
	var inCode = false;
	var inFencedCode = false;
	var codeFence = null;
	var inComment = false;

	var anchorCache = {};

	var uniqueAnchor = function(anchor) {
		var cnt = anchorCache[anchor];
		if (cnt) {
			anchorCache[anchor] = cnt + 1;
			return anchor + '_' + cnt;
		}
		anchorCache[anchor] = 1;
		return anchor;
	};

	s.on('end', function() {
		that.emit('end');
	});

	s.on('data', function(line) {

		row = row + 1;

		var comments = [];
		var lastComment = null;
		var isInComment = function(m) {
			var i, cm;
			for (i = 0; i < comments.length; i++) {
				cm = comments[i];
				if (m.index >= cm.index && m.index < (cm.index + cm[0].length)) {
					return true;
				}
				return false;
			}
		};

		// code

		if (!inComment) {
			if (inFencedCode) {
				if (line === codeFence) {
					codeFence = null;
					inFencedCode = false;
					that.emit('endCode', {
						row: row - 1,
						column: lastLine.length + 1
					});
				} else {
					that.emit('code', {
						row: row,
						column: 1,
						text: line
					});
					lastLine = line;
					return;
				}
			} else {
				if (match(codePattern, line, function(m) {
					if (!inCode && lastLine.trim().length === 0 && m[1].length > 0) {
						inCode = true;
						that.emit('startCode', {
							row: row,
							column: 1
						});
					}
					if (inCode) {
						that.emit('code', {
							row: row,
							column: 1,
							text: m[1]
						});
					}
				}) > 0) {
					if (inCode) {
						lastLine = line;
						return;
					}
				}
				if (inCode) {
					inCode = false;
					that.emit('endCode', {
						row: row - 1,
						column: lastLine.length + 1
					});
				}
				if (match(fencedCodeStartPattern, line, function(m) {
					inFencedCode = true;
					codeFence = m[1];
					that.emit('startCode', {
						row: row + 1,
						column: 1,
						codeAttributes: m[2] || ''
					});
				}) > 0) {
					if (inFencedCode) {
						lastLine = line;
						return;
					}
				}
			}
		}

		// comments

		if (inComment) {
			match(commentEndPattern, line, function(m) {
				comments.push(m);
				if (m[1].length > 0) {
					that.emit('comment', {
						row: row,
						column: m.index + 1,
						text: m[1],
						inline: false
					});
				}
				inComment = false;
				that.emit('endComment', {
					row: row,
					column: m.index + m[0].length + 1,
				});
			});
			if (inComment) {
				that.emit('comment', {
					row: row,
					column: 1,
					text: line,
					inline: false
				});
				return;
			}
		}

		match(commentPattern, line, function(m) {
			that.emit('comment', {
				row: row,
				column: m.index + 1,
				text: m[1],
				inline: true
			});
			comments.push(m);
			lastComment = m.index + m[0].length;
		});

		match(commentStartPattern, line, function(m) {
			if (lastComment !== null && m.index < lastComment) {
				return;
			}
			inComment = true;
			that.emit('startComment', {
				row: row,
				column: m.index + 1
			});
			comments.push(m);
			if (m[1].length > 0) {
				that.emit('comment', {
					row: row,
					column: m.index + 1,
					text: m[1],
					inline: false
				});
			}
		});

		// headline

		match(headlinePattern, line, function(m) {
			if (isInComment(m)) return;
			that.emit('headline', {
				row: row,
				column: m.index + 1,
				level: m[1].length,
				source: m[2],
				text: mdheadline.removeFormat(m[2]),
				anchor: uniqueAnchor(mdheadline.anchor(m[2]))
			});
		}) ||
		match(headline1Pattern, line, function(m) {
			if (isInComment(m)) return;
			that.emit('headline', {
				row: row - 1,
				column: m.index + 1,
				level: 1,
				source: lastLine,
				text: mdheadline.removeFormat(lastLine),
				anchor: uniqueAnchor(mdheadline.anchor(lastLine))
			});
		}) ||
		match(headline2Pattern, line, function(m) {
			if (isInComment(m)) return;
			if (row === 1) return;
			that.emit('headline', {
				row: row - 1,
				column: m.index + 1,
				level: 2,
				source: lastLine,
				text: mdheadline.removeFormat(lastLine),
				anchor: uniqueAnchor(mdheadline.anchor(lastLine))
			});
		});

		// citations

		match(citationPattern, line, function (m) {
			if (isInComment(m)) return;
			that.emit('citation', {
				text: m[2],
				level: m[1].replace(/\s/g, '').length,
				row: row,
				column: 1
			});
		});

		// internal links

		match(internalLinkPattern, line, function(m) {
			if (isInComment(m)) return;
			if (m[2]) {
				that.emit('internal-link', {
					row: row,
					column: m.index > 0 ? m.index + 2 : m.index + 1,
					text: m[1],
					target: m[2],
					targetText: mdheadline.removeFormat(m[2])
				});
			} else {
				that.emit('internal-link', {
					row: row,
					column: m.index > 0 ? m.index + 2 : m.index + 1,
					text: m[1],
					target: m[1],
					targetText: mdheadline.removeFormat(m[1])
				});
			}
		});

		// external links

		match(externalLinkPattern, line, function(m) {
			if (isInComment(m)) return;
			that.emit('link', {
				row: row,
				column: m.index > 0 ? m.index + 2 : m.index + 1,
				text: m[1],
				url: m[2]
			});
		});

		match(urlLinkPattern, line, function(m) {
			if (isInComment(m)) return;
			that.emit('link', {
				row: row,
				column: m.index + 1,
				text: m[1],
				url: m[1]
			});
		});

		lastLine = line.trim();
	});
};
util.inherits(MdParser, EventEmitter);

MdParser.prototype.pause = function() {
	this._inputStream.pause();
};

MdParser.prototype.resume = function() {
	this._inputStream.resume();
};

module.exports = MdParser;<commit_msg>implemented YAML header events<commit_after>var util = require('util');
var EventEmitter = require('events').EventEmitter;
var mdheadline = require('mdheadline');
var lines = require('./lines');

var match = function (re, text, fn) {
	var m;
	var cnt = 0;
	re.lastIndex = 0;
	m = re.exec(text);
	if (re.global) {
		while(m) {
			cnt = cnt + 1;
			fn(m);
			m = re.exec(text);
		}
	} else if (m) {
		cnt = cnt + 1;
		fn(m);
	}
	return cnt;
};

var MdParser = function (input, encoding) {

	var yamlHeaderStart = /^---$/;
	var yamlHeaderEnd = /^-{3}|\.{3}$/;

	var headlinePattern = /^(#+)\s+(.*?)\s*$/;
	var headline1Pattern = /^==+\s*$/;
	var headline2Pattern = /^--+\s*$/;

	var internalLinkPattern = /(?:^|[^\]\)!])\[([^\]]+)\](?:\[([^\]]*)\])?/g;

	var externalLinkPattern = /(?:^|[^\]!])\[([^\]]+)\]\(([^\)]+)\)/g;
	var urlLinkPattern = /<([^>\s]+)>/g;

	var codePattern = /^(?: {4}|\t)(.*)$/;
	var fencedCodeStartPattern = /^(`{3,}|~{3,})\s*(.*?)\s*$/;

	var commentPattern = /<!--(.*?)-->/g;

	var commentStartPattern = /(?:<!--)(?!.*<!--)(.*?)$/;
	var commentEndPattern = /^(.*?)-->/;

	var citationPattern = /^ {0,3}(>[\s>]*)(.*)\s*$/;

	var that = this;

	var s = lines(input, encoding);
	s.pause();
	if (s === undefined) {
		throw 'Invalid input.';
	}
	s.setEncoding('utf8');
	that._inputStream = s;

	var row = 0;
	var lastLine = null;
	var inHeader = false;
	var inCode = false;
	var inFencedCode = false;
	var codeFence = null;
	var inComment = false;

	var anchorCache = {};

	var uniqueAnchor = function(anchor) {
		var cnt = anchorCache[anchor];
		if (cnt) {
			anchorCache[anchor] = cnt + 1;
			return anchor + '_' + cnt;
		}
		anchorCache[anchor] = 1;
		return anchor;
	};

	s.on('end', function() {
		that.emit('end');
	});

	s.on('data', function(line) {

		row = row + 1;

		var comments = [];
		var lastComment = null;
		var isInComment = function(m) {
			var i, cm;
			for (i = 0; i < comments.length; i++) {
				cm = comments[i];
				if (m.index >= cm.index && m.index < (cm.index + cm[0].length)) {
					return true;
				}
				return false;
			}
		};

		// header

		if (row === 1) {
			match(yamlHeaderStart, line, function (m) {
				inHeader = true;
				that.emit('startHeader', {
					row: row + 1,
					column: 1
				});
			});
			if (inHeader) {
				lastLine = line;
				return;
			}
		} else if (row > 1 && inHeader) {
			match(yamlHeaderEnd, line, function (m) {
				inHeader = false;
				that.emit('endHeader', {
					row: row - 1,
					column: lastLine.length + 1
				})
			});
		}
		if (inHeader) {
			that.emit('header', {
				text: line,
				row: row,
				column: 1
			});
			lastLine = line;
			return;
		}

		// code

		if (!inHeader && !inComment) {
			if (inFencedCode) {
				if (line === codeFence) {
					codeFence = null;
					inFencedCode = false;
					that.emit('endCode', {
						row: row - 1,
						column: lastLine.length + 1
					});
				} else {
					that.emit('code', {
						row: row,
						column: 1,
						text: line
					});
					lastLine = line;
					return;
				}
			} else {
				if (match(codePattern, line, function(m) {
					if (!inCode && lastLine.trim().length === 0 && m[1].length > 0) {
						inCode = true;
						that.emit('startCode', {
							row: row,
							column: 1
						});
					}
					if (inCode) {
						that.emit('code', {
							row: row,
							column: 1,
							text: m[1]
						});
					}
				}) > 0) {
					if (inCode) {
						lastLine = line;
						return;
					}
				}
				if (inCode) {
					inCode = false;
					that.emit('endCode', {
						row: row - 1,
						column: lastLine.length + 1
					});
				}
				if (match(fencedCodeStartPattern, line, function(m) {
					inFencedCode = true;
					codeFence = m[1];
					that.emit('startCode', {
						row: row + 1,
						column: 1,
						codeAttributes: m[2] || ''
					});
				}) > 0) {
					if (inFencedCode) {
						lastLine = line;
						return;
					}
				}
			}
		}

		// comments

		if (inComment) {
			match(commentEndPattern, line, function(m) {
				comments.push(m);
				if (m[1].length > 0) {
					that.emit('comment', {
						row: row,
						column: m.index + 1,
						text: m[1],
						inline: false
					});
				}
				inComment = false;
				that.emit('endComment', {
					row: row,
					column: m.index + m[0].length + 1,
				});
			});
			if (inComment) {
				that.emit('comment', {
					row: row,
					column: 1,
					text: line,
					inline: false
				});
				return;
			}
		}

		match(commentPattern, line, function(m) {
			that.emit('comment', {
				row: row,
				column: m.index + 1,
				text: m[1],
				inline: true
			});
			comments.push(m);
			lastComment = m.index + m[0].length;
		});

		match(commentStartPattern, line, function(m) {
			if (lastComment !== null && m.index < lastComment) {
				return;
			}
			inComment = true;
			that.emit('startComment', {
				row: row,
				column: m.index + 1
			});
			comments.push(m);
			if (m[1].length > 0) {
				that.emit('comment', {
					row: row,
					column: m.index + 1,
					text: m[1],
					inline: false
				});
			}
		});

		// headline

		match(headlinePattern, line, function(m) {
			if (isInComment(m)) return;
			that.emit('headline', {
				row: row,
				column: m.index + 1,
				level: m[1].length,
				source: m[2],
				text: mdheadline.removeFormat(m[2]),
				anchor: uniqueAnchor(mdheadline.anchor(m[2]))
			});
		}) ||
		match(headline1Pattern, line, function(m) {
			if (isInComment(m)) return;
			that.emit('headline', {
				row: row - 1,
				column: m.index + 1,
				level: 1,
				source: lastLine,
				text: mdheadline.removeFormat(lastLine),
				anchor: uniqueAnchor(mdheadline.anchor(lastLine))
			});
		}) ||
		match(headline2Pattern, line, function(m) {
			if (isInComment(m)) return;
			if (row === 1) return;
			that.emit('headline', {
				row: row - 1,
				column: m.index + 1,
				level: 2,
				source: lastLine,
				text: mdheadline.removeFormat(lastLine),
				anchor: uniqueAnchor(mdheadline.anchor(lastLine))
			});
		});

		// citations

		match(citationPattern, line, function (m) {
			if (isInComment(m)) return;
			that.emit('citation', {
				text: m[2],
				level: m[1].replace(/\s/g, '').length,
				row: row,
				column: 1
			});
		});

		// internal links

		match(internalLinkPattern, line, function(m) {
			if (isInComment(m)) return;
			if (m[2]) {
				that.emit('internal-link', {
					row: row,
					column: m.index > 0 ? m.index + 2 : m.index + 1,
					text: m[1],
					target: m[2],
					targetText: mdheadline.removeFormat(m[2])
				});
			} else {
				that.emit('internal-link', {
					row: row,
					column: m.index > 0 ? m.index + 2 : m.index + 1,
					text: m[1],
					target: m[1],
					targetText: mdheadline.removeFormat(m[1])
				});
			}
		});

		// external links

		match(externalLinkPattern, line, function(m) {
			if (isInComment(m)) return;
			that.emit('link', {
				row: row,
				column: m.index > 0 ? m.index + 2 : m.index + 1,
				text: m[1],
				url: m[2]
			});
		});

		match(urlLinkPattern, line, function(m) {
			if (isInComment(m)) return;
			that.emit('link', {
				row: row,
				column: m.index + 1,
				text: m[1],
				url: m[1]
			});
		});

		lastLine = line.trim();
	});
};
util.inherits(MdParser, EventEmitter);

MdParser.prototype.pause = function() {
	this._inputStream.pause();
};

MdParser.prototype.resume = function() {
	this._inputStream.resume();
};

module.exports = MdParser;<|endoftext|>


<commit_before>/**
 * @module og/layer/Material
 */

'use strict';

const Material = function (segment, layer) {
    this.segment = segment;
    this.layer = layer;
    this.isReady = false;
    this.isLoading = false;
    this.texture = null;
    this.pickingMask = null;
    this.image = null;
    this.textureExists = false;
    this.appliedNodeId = 0;
    this.texOffset = [0.0, 0.0, 1.0, 1.0];
    this.loadingAttempts = 0;

    // vector data
    this._updateTexture = null;
    this._updatePickingMask = null;
    this.pickingReady = false;
};

Material.prototype.assignLayer = function (layer) {
    this.layer = layer;
};

Material.prototype.abortLoading = function () {
    this.layer.abortMaterialLoading(this);
};

Material.prototype.applyImage = function (img) {
    if (this.segment.initialized) {
        this._updateTexture = null;
        this.image = img;
        this.texture = this.segment.handler.createTexture(img);
        this.appliedNodeId = this.segment.node.nodeId;
        this.isReady = true;
        this.pickingReady = true;
        this.textureExists = true;
        this.isLoading = false;
        this.texOffset = [0.0, 0.0, 1.0, 1.0];
    }
};

Material.prototype.applyTexture = function (texture, pickingMask) {
    if (this.segment.initialized) {
        this.texture = texture;
        this._updateTexture = null;
        this.pickingMask = pickingMask || null;
        this._updatePickingMask = null;
        this.isReady = true;
        this.pickingReady = true;
        this.textureExists = true;
        this.isLoading = false;
        this.appliedNodeId = this.segment.node.nodeId;
        this.texOffset = [0.0, 0.0, 1.0, 1.0];
    }
};

Material.prototype.textureNotExists = function () {
    if (this.segment.initialized) {
        this.pickingReady = true;
        this.isLoading = false;
        this.isReady = true;
        this.textureExists = false;
    }
};

Material.prototype.clear = function () {
    this.loadingAttempts = 0;
    this.layer.clearMaterial(this);
};

export { Material };<commit_msg>Removed segment tile image from storage.<commit_after>/**
 * @module og/layer/Material
 */

'use strict';

const Material = function (segment, layer) {
    this.segment = segment;
    this.layer = layer;
    this.isReady = false;
    this.isLoading = false;
    this.texture = null;
    this.pickingMask = null;
    //this.image = null;
    this.textureExists = false;
    this.appliedNodeId = 0;
    this.texOffset = [0.0, 0.0, 1.0, 1.0];
    this.loadingAttempts = 0;

    // vector data
    this._updateTexture = null;
    this._updatePickingMask = null;
    this.pickingReady = false;
};

Material.prototype.assignLayer = function (layer) {
    this.layer = layer;
};

Material.prototype.abortLoading = function () {
    this.layer.abortMaterialLoading(this);
};

Material.prototype.applyImage = function (img) {
    if (this.segment.initialized) {
        this._updateTexture = null;
        //this.image = img;
        this.texture = this.segment.handler.createTexture(img);
        this.appliedNodeId = this.segment.node.nodeId;
        this.isReady = true;
        this.pickingReady = true;
        this.textureExists = true;
        this.isLoading = false;
        this.texOffset = [0.0, 0.0, 1.0, 1.0];
    }
};

Material.prototype.applyTexture = function (texture, pickingMask) {
    if (this.segment.initialized) {
        this.texture = texture;
        this._updateTexture = null;
        this.pickingMask = pickingMask || null;
        this._updatePickingMask = null;
        this.isReady = true;
        this.pickingReady = true;
        this.textureExists = true;
        this.isLoading = false;
        this.appliedNodeId = this.segment.node.nodeId;
        this.texOffset = [0.0, 0.0, 1.0, 1.0];
    }
};

Material.prototype.textureNotExists = function () {
    if (this.segment.initialized) {
        this.pickingReady = true;
        this.isLoading = false;
        this.isReady = true;
        this.textureExists = false;
    }
};

Material.prototype.clear = function () {
    this.loadingAttempts = 0;
    this.layer.clearMaterial(this);
};

export { Material };<|endoftext|>


<commit_before>// FIXME keep cluster cache by resolution ?
// FIXME distance not respected because of the centroid

goog.provide('ol.source.Cluster');

goog.require('ol');
goog.require('ol.asserts');
goog.require('ol.Feature');
goog.require('ol.coordinate');
goog.require('ol.events.EventType');
goog.require('ol.extent');
goog.require('ol.geom.Point');
goog.require('ol.source.Vector');

/**
 * @classdesc
 * Layer source to cluster vector data. Works out of the box with point
 * geometries. For other geometry types, or if not all geometries should be
 * considered for clustering, a custom `geometryFunction` can be defined.
 *
 * @constructor
 * @param {olx.source.ClusterOptions} options Constructor options.
 * @extends {ol.source.Vector}
 * @api
 */
ol.source.Cluster = function(options) {
  ol.source.Vector.call(this, {
    attributions: options.attributions,
    extent: options.extent,
    logo: options.logo,
    projection: options.projection,
    wrapX: options.wrapX
  });

  /**
   * @type {number|undefined}
   * @private
   */
  this.resolution_ = undefined;

  /**
   * @type {number}
   * @private
   */
  this.distance_ = options.distance !== undefined ? options.distance : 20;

  /**
   * @type {Array.<ol.Feature>}
   * @private
   */
  this.features_ = [];

  /**
   * @param {ol.Feature} feature Feature.
   * @return {ol.geom.Point} Cluster calculation point.
   */
  this.geometryFunction_ = options.geometryFunction || function(feature) {
    var geometry = /** @type {ol.geom.Point} */ (feature.getGeometry());
    ol.asserts.assert(geometry instanceof ol.geom.Point,
        10); // The default `geometryFunction` can only handle `ol.geom.Point` geometries
    return geometry;
  };

  /**
   * @type {ol.source.Vector}
   * @private
   */
  this.source_ = options.source;

  this.source_.on(ol.events.EventType.CHANGE,
      ol.source.Cluster.prototype.refresh_, this);
};
ol.inherits(ol.source.Cluster, ol.source.Vector);

/**
 * Get the distance in pixels between clusters.
 * @return {number} Distance.
 * @api
 */
ol.source.Cluster.prototype.getDistance = function() {
  return this.distance_;
};

/**
 * Get a reference to the wrapped source.
 * @return {ol.source.Vector} Source.
 * @api
 */
ol.source.Cluster.prototype.getSource = function() {
  return this.source_;
};

/**
 * @inheritDoc
 */
ol.source.Cluster.prototype.loadFeatures = function(extent, resolution,
    projection) {
  this.source_.loadFeatures(extent, resolution, projection);
  if (resolution !== this.resolution_) {
    this.clear();
    this.resolution_ = resolution;
    this.cluster_();
    this.addFeatures(this.features_);
  }
};

/**
 * Set the distance in pixels between clusters.
 * @param {number} distance The distance in pixels.
 * @api
 */
ol.source.Cluster.prototype.setDistance = function(distance) {
  this.distance_ = distance;
  this.refresh_();
};

/**
 * handle the source changing
 * @private
 */
ol.source.Cluster.prototype.refresh_ = function() {
  this.clear();
  this.cluster_();
  this.addFeatures(this.features_);
  this.changed();
};

/**
 * @private
 */
ol.source.Cluster.prototype.cluster_ = function() {
  if (this.resolution_ === undefined) {
    return;
  }
  this.features_.length = 0;
  var extent = ol.extent.createEmpty();
  var mapDistance = this.distance_ * this.resolution_;
  var features = this.source_.getFeatures();

  /**
   * @type {!Object.<string, boolean>}
   */
  var clustered = {};

  for (var i = 0, ii = features.length; i < ii; i++) {
    var feature = features[i];
    if (!(ol.getUid(feature).toString() in clustered)) {
      var geometry = this.geometryFunction_(feature);
      if (geometry) {
        var coordinates = geometry.getCoordinates();
        ol.extent.createOrUpdateFromCoordinate(coordinates, extent);
        ol.extent.buffer(extent, mapDistance, extent);

        var neighbors = this.source_.getFeaturesInExtent(extent);
        neighbors = neighbors.filter(function(neighbor) {
          var uid = ol.getUid(neighbor).toString();
          if (!(uid in clustered)) {
            clustered[uid] = true;
            return true;
          } else {
            return false;
          }
        });
        this.features_.push(this.createCluster_(neighbors));
      }
    }
  }
};

/**
 * @param {Array.<ol.Feature>} features Features
 * @return {ol.Feature} The cluster feature.
 * @private
 */
ol.source.Cluster.prototype.createCluster_ = function(features) {
  var centroid = [0, 0];
  for (var i = features.length - 1; i >= 0; --i) {
    var geometry = this.geometryFunction_(features[i]);
    if (geometry) {
      ol.coordinate.add(centroid, geometry.getCoordinates());
    } else {
      features.splice(i, 1);
    }
  }
  ol.coordinate.scale(centroid, 1 / features.length);

  var cluster = new ol.Feature(new ol.geom.Point(centroid));
  cluster.set('features', features);
  return cluster;
};
<commit_msg>Make ol.source.Cluster extensible<commit_after>// FIXME keep cluster cache by resolution ?
// FIXME distance not respected because of the centroid

goog.provide('ol.source.Cluster');

goog.require('ol');
goog.require('ol.asserts');
goog.require('ol.Feature');
goog.require('ol.coordinate');
goog.require('ol.events.EventType');
goog.require('ol.extent');
goog.require('ol.geom.Point');
goog.require('ol.source.Vector');

/**
 * @classdesc
 * Layer source to cluster vector data. Works out of the box with point
 * geometries. For other geometry types, or if not all geometries should be
 * considered for clustering, a custom `geometryFunction` can be defined.
 *
 * @constructor
 * @param {olx.source.ClusterOptions} options Constructor options.
 * @extends {ol.source.Vector}
 * @api
 */
ol.source.Cluster = function(options) {
  ol.source.Vector.call(this, {
    attributions: options.attributions,
    extent: options.extent,
    logo: options.logo,
    projection: options.projection,
    wrapX: options.wrapX
  });

  /**
   * @type {number|undefined}
   * @protected
   */
  this.resolution = undefined;

  /**
   * @type {number}
   * @protected
   */
  this.distance = options.distance !== undefined ? options.distance : 20;

  /**
   * @type {Array.<ol.Feature>}
   * @protected
   */
  this.features = [];

  /**
   * @param {ol.Feature} feature Feature.
   * @return {ol.geom.Point} Cluster calculation point.
   * @protected
   */
  this.geometryFunction = options.geometryFunction || function(feature) {
    var geometry = /** @type {ol.geom.Point} */ (feature.getGeometry());
    ol.asserts.assert(geometry instanceof ol.geom.Point,
        10); // The default `geometryFunction` can only handle `ol.geom.Point` geometries
    return geometry;
  };

  /**
   * @type {ol.source.Vector}
   * @protected
   */
  this.source = options.source;

  this.source.on(ol.events.EventType.CHANGE,
      ol.source.Cluster.prototype.refresh, this);
};
ol.inherits(ol.source.Cluster, ol.source.Vector);

/**
 * Get the distance in pixels between clusters.
 * @return {number} Distance.
 * @api
 */
ol.source.Cluster.prototype.getDistance = function() {
  return this.distance;
};

/**
 * Get a reference to the wrapped source.
 * @return {ol.source.Vector} Source.
 * @api
 */
ol.source.Cluster.prototype.getSource = function() {
  return this.source;
};

/**
 * @inheritDoc
 */
ol.source.Cluster.prototype.loadFeatures = function(extent, resolution,
    projection) {
  this.source.loadFeatures(extent, resolution, projection);
  if (resolution !== this.resolution) {
    this.clear();
    this.resolution = resolution;
    this.cluster();
    this.addFeatures(this.features);
  }
};

/**
 * Set the distance in pixels between clusters.
 * @param {number} distance The distance in pixels.
 * @api
 */
ol.source.Cluster.prototype.setDistance = function(distance) {
  this.distance = distance;
  this.refresh();
};

/**
 * handle the source changing
 * @override
 */
ol.source.Cluster.prototype.refresh = function() {
  this.clear();
  this.cluster();
  this.addFeatures(this.features);
  ol.source.Vector.prototype.refresh.call(this);
};

/**
 * @protected
 */
ol.source.Cluster.prototype.cluster = function() {
  if (this.resolution === undefined) {
    return;
  }
  this.features.length = 0;
  var extent = ol.extent.createEmpty();
  var mapDistance = this.distance * this.resolution;
  var features = this.source.getFeatures();

  /**
   * @type {!Object.<string, boolean>}
   */
  var clustered = {};

  for (var i = 0, ii = features.length; i < ii; i++) {
    var feature = features[i];
    if (!(ol.getUid(feature).toString() in clustered)) {
      var geometry = this.geometryFunction(feature);
      if (geometry) {
        var coordinates = geometry.getCoordinates();
        ol.extent.createOrUpdateFromCoordinate(coordinates, extent);
        ol.extent.buffer(extent, mapDistance, extent);

        var neighbors = this.source.getFeaturesInExtent(extent);
        neighbors = neighbors.filter(function(neighbor) {
          var uid = ol.getUid(neighbor).toString();
          if (!(uid in clustered)) {
            clustered[uid] = true;
            return true;
          } else {
            return false;
          }
        });
        this.features.push(this.createCluster(neighbors));
      }
    }
  }
};

/**
 * @param {Array.<ol.Feature>} features Features
 * @return {ol.Feature} The cluster feature.
 * @protected
 */
ol.source.Cluster.prototype.createCluster = function(features) {
  var centroid = [0, 0];
  for (var i = features.length - 1; i >= 0; --i) {
    var geometry = this.geometryFunction(features[i]);
    if (geometry) {
      ol.coordinate.add(centroid, geometry.getCoordinates());
    } else {
      features.splice(i, 1);
    }
  }
  ol.coordinate.scale(centroid, 1 / features.length);

  var cluster = new ol.Feature(new ol.geom.Point(centroid));
  cluster.set('features', features);
  return cluster;
};
<|endoftext|>


<commit_before>
// Require BufferPack
var MobiBook = function(data) {
    if (!(this instanceof MobiBook)) {
        return new MobiBook(data); // protect against calls without new
    }
    this.data = data;
    //  @@ throw if no BufferPack?

    // First thing should be a Palm Database Format header
    var offset = 0;
    this.pdfHdr = BufferPack.unpack("32s(name)H(attribs)H(version)I(created)I(modified)I(backedup)I(modification)I(appInfoOff)I(sortInfoOff)4s(type)4s(creator)I(uniqueId)I(nextRecord)H(numRecords)",
                                    data, offset);
    offset += 78;
    this.pdfHdr.recordInfo = [];
    for (var ii = 0; ii < this.pdfHdr.numRecords; ii++) {
        this.pdfHdr.recordInfo.push(BufferPack.unpack("I(offset)B(attribs)B(idHigh)H(idLow)", data, offset));
        offset += 8;
        if (ii > 0) {
            // Assume records are in order
            this.pdfHdr.recordInfo[ii-1].recordLen = (this.pdfHdr.recordInfo[ii].offset - this.pdfHdr.recordInfo[ii-1].offset);
        }
    }
    // Assume last record goes to end of file
    this.pdfHdr.recordInfo[this.pdfHdr.numRecords - 1].recordLen = (data.length - this.pdfHdr.recordInfo[this.pdfHdr.numRecords - 1].offset)

    // Next, app info area if present (unlikely for ebooks)
    // Next, sort info area if present (unlikely for ebooks)
    // N x records
    this.pdfHdr.record = [];

    // Record zero
    // Record0: First is a PalmDoc header
    this.palmDocHdr = BufferPack.unpack("H(compression)H(reserved)I(uncompressedTextLength)H(recordCount)H(maxRecordSize)H(encryption)H(unknown)",
                                        data, this.pdfHdr.recordInfo[0].offset);
    var COMPRESSION = {
        1 : "No compression",
        2 : "PalmDOC compression"
    };
    // Record0: Next is a MOBI header
    if (this.pdfHdr.recordInfo[0].recordLen > 16) {
        this.mobiHdr = BufferPack.unpack("4s(ident)I(hdrLen)I(type)I(encoding)I(uniqueId)I(fileVersion)I(orthoIndex)I(infectionIndex)I(indexNames)I(indexKeys)I(extraIndex0)I(extraIndex1)I(extraIndex2)I(extraIndex3)I(extraIndex4)I(extraIndex5)I(firstNonBookIndex)I(fullNameOffset)I(fullNameLen)I(locale)I(inputLang)I(outputLang)I(minVersion)I(firstImageIndex)I(huffmanRecordOffset)I(huffmanRecordCount)I(huffmanTableOffset)I(huffmanTableLength)I(exthFlags)",
                                         data, this.pdfHdr.recordInfo[0].offset + 16);
        if (this.mobiHdr.ident != "MOBI") throw Error("Unexpected identifier " + this.mobiHdr.ident + " in MOBI header");
        var MOBITYPE = {
            2 : "Mobipocket Book",
            3 : "PalmDoc Book",
            4 : "Audio",
            232 : "Mobipocket generated by kindlegen1.2",
            248 : "KF8: generated by kindlegen2",
            257 : "News",
            258 : "News_Feed",
            259 : "News_Magazine",
            513 : "PICS",
            514 : "WORD",
            515 : "XLS",
            516 : "PPT",
            517 : "TEXT",
            518 : "HTML"
        };
        var ENCODING = {
            1252: "CP1252",
            65001: "UTF-8"
        };
        var LOCALE_LANG = {
            9 : "English"
        }
        var LOCALE_DIALECT = {
            8 : "British",
            4 : "American"
        }
    }
    // There may be more MOBI header contents
    if (this.pdfHdr.recordInfo[0].recordLen > 132) {
        var moreMobiHdr = BufferPack.unpack("32s(unknownA)I(unknownB)I(drmOffset)I(drmCount)I(drmSize)I(drmFlags)8s(unknownC)H(firstContentRecord)H(lastContentRecord)I(unknownD)I(fcisRecordNumber)I(fcisRecordCount)I(flisRecordNumber)I(flisRecordCount)8s(unknownE)I(unknownF)I(firstCompilationDataSectionCount)I(numCompilationDataSections)I(unknownG)I(extraRecordDataFlags)I(indxRecordOffset)",
                                            data, this.pdfHdr.recordInfo[0].offset + 132);
        for (var k in moreMobiHdr) {
            this.mobiHdr[k] = moreMobiHdr[k];
        }
    }
    var titleOffset = (this.pdfHdr.recordInfo[0].offset + this.mobiHdr.fullNameOffset);
    if ((titleOffset + this.mobiHdr.fullNameLen) <= data.length) {
        this.title = String.fromCharCode.apply(String, data.slice(titleOffset,
                                                                  titleOffset + this.mobiHdr.fullNameLen));
    }

    // Record0: Next is a possible EXTH header
    var EXTH_PRESENT_FLAG = 0x40
    if ((this.mobiHdr.exthFlags & EXTH_PRESENT_FLAG) &&
        (this.pdfHdr.recordInfo[0].recordLen > 248)) {
        this.exthHdr = BufferPack.unpack("4s(ident)I(hdrLen)I(numRecords)",
                                         data, this.pdfHdr.recordInfo[0].offset + 248);
        if (this.exthHdr.ident != "EXTH") throw Error("Unexpected identifier " + this.exthHdr.ident + " in EXTH header");
        this.exthHdr.record = [];
        offset = this.pdfHdr.recordInfo[0].offset + 260;
        var EXTH_RECORD_TYPE = {
            1: "drm_server_id",
            2: "drm_commerce_id",
            3: "drm_ebookbase_book_id",
            100: "author",
            101: "publisher",
            102: "imprint",
            103: "description",
            104: "isbn",
            105: "subject",
            106: "publishingdate",
            107: "review",
            108: "contributor",
            109: "rights",
            110: "subjectcode",
            111: "type",
            112: "source",
            113: "asin",
            114: "versionnumber",
            115: "sample",
            116: "startreading",
            117: "adult",
            118: "retail price",
            119: "retail price currency",
            200: "Dictionary short name",
            201: "coveroffset",
            202: "thumboffset",
            203: "hasfakecover",
            204: "Creator Software",
            205: "Creator Major Version",
            206: "Creator Minor Version",
            207: "Creator Build Number",
            208: "watermark",
            209: "tamper proof keys",
            300: "fontsignature",
            401: "clippinglimit",
            402: "publisherlimit",
            403: "Unknown",
            404: "ttsflag",
            405: "Unknown",
            406: "Unknown",
            407: "Unknown",
            450: "Unknown",
            451: "Unknown",
            452: "Unknown",
            453: "Unknown",
            501: "cdetype",
            502: "lastupdatetime",
            503: "updatedtitle",
            504: "asin",
            524: "language"
        };

        for (var ii = 0; ii < this.exthHdr.numRecords; ii++) {
            var record = BufferPack.unpack("I(type)I(len)", data, offset);
            record.data = BufferPack.unpack((record.len - 8) + "s", data, offset + 8)[0];
            this.exthHdr.record.push(record);
            offset += record.len;
        }
    }

    // Record0: Then some other stuff

    // INDX???
    // TAGX???

    // Record1..RecordN
    for (var ii = 1; ii <  this.pdfHdr.recordInfo.length; ii++) {
        var info = this.pdfHdr.recordInfo[ii];
        if (ii >= 1) {
            var text = MobiBook.palmDocUncompress(data, info.offset, info.offset + info.recordLen);
            console.log(text);
        }
    }
};

MobiBook.dateConvert = function(timestamp) {
    // Timestamp is relative to an epoch of either 1904-01-01 (as uint32) or 1970-01-01.
    if (timestamp & 0x80000000) {
        return new Date((timestamp-2082844800) * 1000);
    } else {
        return new Date(timestamp * 1000);
    }
};

// Read a variable width forward-encoded integer starting at data[offset]
MobiBook.readInteger = function(data, offset) {
    var value = 0;
    while (true) {
        value = ((value << 7) | (data[offset] & 0x7F));
        if ((data[offset] & 0x80) !== 0) {
            return value;
        }
        offset++;
    }
    // @@@ need to return consumed count or new offset value
}

// Read a variable width backward-encoded integer ending at data[offset-1]
MobiBook.readBackwardInteger = function(data, offset) {
    var value = 0;
    offset--;
    var ii = 0;
    while (true) {
        value = ((data[offset] & 0x7F) << (7*ii)) | value;
        if ((data[offset] & 0x80) !== 0) {
            return value;
        }
        offset--;
        ii++;
    }
    // @@@ need to return consumed count or new offset value
}

MobiBook.palmDocUncompress = function(data, offset, boundary) {
    var ii;
    var result = [];
    var ASCII_SPACE = 0x20;
    while (offset < boundary) {
        if (data[offset] === 0) {
            result.push(0);
            offset++;
        } else if (data[offset] >= 0x01 && data[offset] <= 0x08) {
            var len = data[offset];
            offset++;
            for (ii = 0; ii < len; ii++) {
                result.push(data[offset + ii]);
            }
            offset += len;
        } else if (data[offset] >= 0x09 && data[offset] <= 0x7f) {
            result.push(data[offset]);
            offset++;
        } else if (data[offset] >= 0x80 && data[offset] <= 0xbf) {
            var dist_len = (((data[offset] & 0x3F) << 8) | data[offset+1]);  // 14 bit distance+length
            var dist = (dist_len >> 3);  // 11 bit offset backwards
            var len = 3 + (dist_len & 0x07);  // 3 bit length
            var dest_offset = result.length - dist;
            if (dest_offset < 0) {
                console.log("*************************** dist_len=" + toHex(dist_len) + " : dist=" + dist + " len=" + len + " currentlength=" + result.length);
                // @@@ throw Error("Compression offset before start of data");
            } else {
                for (ii = 0; ii < len; ii++) {
                    result.push(result[dest_offset + ii]);
                }
            }
            offset += 2;
        } else {  // data[offset] >= 0xc0
            result.push(ASCII_SPACE);
            result.push(data[offset] ^ 0x80);
            offset++;
        }
    }
    return String.fromCharCode.apply(String, result);
}

// @@@ temporary for debugging:
function toHex(number) {
    var hexes = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
    var hex = '';
    var nibble;
    do {
        nibble = number & 0x0f;
        number = number >> 4;
        hex = hexes[nibble] + hex;
    } while (number);
    return '0x'+hex;
}

MobiBook.prototype.creationDate = function() {
    return MobiBook.dateConvert(this.pdfHdr.created);
};

MobiBook.prototype.modificationDate = function() {
    return MobiBook.dateConvert(this.pdfHdr.modified);
};

MobiBook.prototype.backupDate = function() {
    return MobiBook.dateConvert(this.pdfHdr.backedup);
};
<commit_msg>Allow for extra non-text at end of records<commit_after>
// Require BufferPack
var MobiBook = function(data) {
    if (!(this instanceof MobiBook)) {
        return new MobiBook(data); // protect against calls without new
    }
    this.data = data;
    //  @@ throw if no BufferPack?

    // First thing should be a Palm Database Format header
    var offset = 0;
    this.pdfHdr = BufferPack.unpack("32s(name)H(attribs)H(version)I(created)I(modified)I(backedup)I(modification)I(appInfoOff)I(sortInfoOff)4s(type)4s(creator)I(uniqueId)I(nextRecord)H(numRecords)",
                                    data, offset);
    offset += 78;
    this.pdfHdr.recordInfo = [];
    for (var ii = 0; ii < this.pdfHdr.numRecords; ii++) {
        this.pdfHdr.recordInfo.push(BufferPack.unpack("I(offset)B(attribs)B(idHigh)H(idLow)", data, offset));
        offset += 8;
        if (ii > 0) {
            // Assume records are in order
            this.pdfHdr.recordInfo[ii-1].recordLen = (this.pdfHdr.recordInfo[ii].offset - this.pdfHdr.recordInfo[ii-1].offset);
        }
    }
    // Assume last record goes to end of file
    this.pdfHdr.recordInfo[this.pdfHdr.numRecords - 1].recordLen = (data.length - this.pdfHdr.recordInfo[this.pdfHdr.numRecords - 1].offset)

    // Next, app info area if present (unlikely for ebooks)
    // Next, sort info area if present (unlikely for ebooks)
    // N x records
    this.pdfHdr.record = [];

    // Record zero
    // Record0: First is a PalmDoc header
    this.palmDocHdr = BufferPack.unpack("H(compression)H(reserved)I(uncompressedTextLength)H(recordCount)H(maxRecordSize)H(encryption)H(unknown)",
                                        data, this.pdfHdr.recordInfo[0].offset);
    var COMPRESSION = {
        1 : "No compression",
        2 : "PalmDOC compression"
    };
    // Record0: Next is a MOBI header
    if (this.pdfHdr.recordInfo[0].recordLen > 16) {
        this.mobiHdr = BufferPack.unpack("4s(ident)I(hdrLen)I(type)I(encoding)I(uniqueId)I(fileVersion)I(orthoIndex)I(infectionIndex)I(indexNames)I(indexKeys)I(extraIndex0)I(extraIndex1)I(extraIndex2)I(extraIndex3)I(extraIndex4)I(extraIndex5)I(firstNonBookIndex)I(fullNameOffset)I(fullNameLen)I(locale)I(inputLang)I(outputLang)I(minVersion)I(firstImageIndex)I(huffmanRecordOffset)I(huffmanRecordCount)I(huffmanTableOffset)I(huffmanTableLength)I(exthFlags)",
                                         data, this.pdfHdr.recordInfo[0].offset + 16);
        if (this.mobiHdr.ident != "MOBI") throw Error("Unexpected identifier " + this.mobiHdr.ident + " in MOBI header");
        var MOBITYPE = {
            2 : "Mobipocket Book",
            3 : "PalmDoc Book",
            4 : "Audio",
            232 : "Mobipocket generated by kindlegen1.2",
            248 : "KF8: generated by kindlegen2",
            257 : "News",
            258 : "News_Feed",
            259 : "News_Magazine",
            513 : "PICS",
            514 : "WORD",
            515 : "XLS",
            516 : "PPT",
            517 : "TEXT",
            518 : "HTML"
        };
        var ENCODING = {
            1252: "CP1252",
            65001: "UTF-8"
        };
        var LOCALE_LANG = {
            9 : "English"
        }
        var LOCALE_DIALECT = {
            8 : "British",
            4 : "American"
        }
    }
    // There may be more MOBI header contents
    if (this.pdfHdr.recordInfo[0].recordLen > 132) {
        var moreMobiHdr = BufferPack.unpack("32s(unknownA)I(unknownB)I(drmOffset)I(drmCount)I(drmSize)I(drmFlags)8s(unknownC)H(firstContentRecord)H(lastContentRecord)I(unknownD)I(fcisRecordNumber)I(fcisRecordCount)I(flisRecordNumber)I(flisRecordCount)8s(unknownE)I(unknownF)I(firstCompilationDataSectionCount)I(numCompilationDataSections)I(unknownG)I(extraRecordDataFlags)I(indxRecordOffset)",
                                            data, this.pdfHdr.recordInfo[0].offset + 132);
        for (var k in moreMobiHdr) {
            this.mobiHdr[k] = moreMobiHdr[k];
        }
    }
    var titleOffset = (this.pdfHdr.recordInfo[0].offset + this.mobiHdr.fullNameOffset);
    if ((titleOffset + this.mobiHdr.fullNameLen) <= data.length) {
        this.title = String.fromCharCode.apply(String, data.slice(titleOffset,
                                                                  titleOffset + this.mobiHdr.fullNameLen));
    }

    // Record0: Next is a possible EXTH header
    var EXTH_PRESENT_FLAG = 0x40
    if ((this.mobiHdr.exthFlags & EXTH_PRESENT_FLAG) &&
        (this.pdfHdr.recordInfo[0].recordLen > 248)) {
        this.exthHdr = BufferPack.unpack("4s(ident)I(hdrLen)I(numRecords)",
                                         data, this.pdfHdr.recordInfo[0].offset + 248);
        if (this.exthHdr.ident != "EXTH") throw Error("Unexpected identifier " + this.exthHdr.ident + " in EXTH header");
        this.exthHdr.record = [];
        offset = this.pdfHdr.recordInfo[0].offset + 260;
        var EXTH_RECORD_TYPE = {
            1: "drm_server_id",
            2: "drm_commerce_id",
            3: "drm_ebookbase_book_id",
            100: "author",
            101: "publisher",
            102: "imprint",
            103: "description",
            104: "isbn",
            105: "subject",
            106: "publishingdate",
            107: "review",
            108: "contributor",
            109: "rights",
            110: "subjectcode",
            111: "type",
            112: "source",
            113: "asin",
            114: "versionnumber",
            115: "sample",
            116: "startreading",
            117: "adult",
            118: "retail price",
            119: "retail price currency",
            200: "Dictionary short name",
            201: "coveroffset",
            202: "thumboffset",
            203: "hasfakecover",
            204: "Creator Software",
            205: "Creator Major Version",
            206: "Creator Minor Version",
            207: "Creator Build Number",
            208: "watermark",
            209: "tamper proof keys",
            300: "fontsignature",
            401: "clippinglimit",
            402: "publisherlimit",
            403: "Unknown",
            404: "ttsflag",
            405: "Unknown",
            406: "Unknown",
            407: "Unknown",
            450: "Unknown",
            451: "Unknown",
            452: "Unknown",
            453: "Unknown",
            501: "cdetype",
            502: "lastupdatetime",
            503: "updatedtitle",
            504: "asin",
            524: "language"
        };

        for (var ii = 0; ii < this.exthHdr.numRecords; ii++) {
            var record = BufferPack.unpack("I(type)I(len)", data, offset);
            record.data = BufferPack.unpack((record.len - 8) + "s", data, offset + 8)[0];
            this.exthHdr.record.push(record);
            offset += record.len;
        }
    }

    // Record0: Then some other stuff

    // INDX???
    // TAGX???

    // Record1..RecordN
    for (var ii = 1; ii <  this.pdfHdr.recordInfo.length; ii++) {
        var info = this.pdfHdr.recordInfo[ii];
        var len = info.recordLen;
        var RECORD_TRAILING_DATA_FLAGS = 0x07;
        if (this.mobiHdr.extraRecordDataFlags & RECORD_TRAILING_DATA_FLAGS) {
            // There is trailing <data><size> at the end of each record
            var extraDataLen = MobiBook.readBackwardInteger(data, info.offset + info.recordLen);
            len -= extraDataLen;
        }
        if (ii >= 1) {
            var text = MobiBook.palmDocUncompress(data, info.offset, info.offset + len);
            console.log(text);
        }
    }
};

MobiBook.dateConvert = function(timestamp) {
    // Timestamp is relative to an epoch of either 1904-01-01 (as uint32) or 1970-01-01.
    if (timestamp & 0x80000000) {
        return new Date((timestamp-2082844800) * 1000);
    } else {
        return new Date(timestamp * 1000);
    }
};

// Read a variable width forward-encoded integer starting at data[offset]
MobiBook.readInteger = function(data, offset) {
    var value = 0;
    while (true) {
        value = ((value << 7) | (data[offset] & 0x7F));
        if ((data[offset] & 0x80) !== 0) {
            return value;
        }
        offset++;
    }
    // @@@ need to return consumed count or new offset value
}

// Read a variable width backward-encoded integer ending at data[offset-1]
MobiBook.readBackwardInteger = function(data, offset) {
    var value = 0;
    offset--;
    var ii = 0;
    while (true) {
        value = ((data[offset] & 0x7F) << (7*ii)) | value;
        if ((data[offset] & 0x80) !== 0) {
            return value;
        }
        offset--;
        ii++;
    }
    // @@@ need to return consumed count or new offset value
}

MobiBook.palmDocUncompress = function(data, offset, boundary) {
    var ii;
    var result = [];
    var ASCII_SPACE = 0x20;
    while (offset < boundary) {
        if (data[offset] === 0) {
            result.push(0);
            offset++;
        } else if (data[offset] >= 0x01 && data[offset] <= 0x08) {
            var len = data[offset];
            offset++;
            for (ii = 0; ii < len; ii++) {
                result.push(data[offset + ii]);
            }
            offset += len;
        } else if (data[offset] >= 0x09 && data[offset] <= 0x7f) {
            result.push(data[offset]);
            offset++;
        } else if (data[offset] >= 0x80 && data[offset] <= 0xbf) {
            var dist_len = (((data[offset] & 0x3F) << 8) | data[offset+1]);  // 14 bit distance+length
            var dist = (dist_len >> 3);  // 11 bit offset backwards
            var len = 3 + (dist_len & 0x07);  // 3 bit length
            var dest_offset = result.length - dist;
            if (dest_offset < 0) {
                console.log("*************************** dist_len=" + toHex(dist_len) + " : dist=" + dist + " len=" + len + " currentlength=" + result.length);
                // @@@ throw Error("Compression offset before start of data");
            } else {
                for (ii = 0; ii < len; ii++) {
                    result.push(result[dest_offset + ii]);
                }
            }
            offset += 2;
        } else {  // data[offset] >= 0xc0
            result.push(ASCII_SPACE);
            result.push(data[offset] ^ 0x80);
            offset++;
        }
    }
    return String.fromCharCode.apply(String, result);
}

// @@@ temporary for debugging:
function toHex(number) {
    var hexes = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
    var hex = '';
    var nibble;
    do {
        nibble = number & 0x0f;
        number = number >> 4;
        hex = hexes[nibble] + hex;
    } while (number);
    return '0x'+hex;
}

MobiBook.prototype.creationDate = function() {
    return MobiBook.dateConvert(this.pdfHdr.created);
};

MobiBook.prototype.modificationDate = function() {
    return MobiBook.dateConvert(this.pdfHdr.modified);
};

MobiBook.prototype.backupDate = function() {
    return MobiBook.dateConvert(this.pdfHdr.backedup);
};
<|endoftext|>


<commit_before>var Colors = {
    red:0xf25346,
    white:0xd8d0d1,
    brown:0x59332e,
    pink:0xF5986E,
    brownDark:0x23190f,
    blue:0x68c3c0,
};
window.addEventListener('load', init, false);

var scene,
camera, fieldOfView, aspectRatio, nearPlane, farPlane, HEIGHT, WIDTH,
renderer, container, controls, clock, raycaster;

function init(event) {

    // set up the scene, the camera and the renderer
    createScene();

    // add the lights
    createLights();

    // add the objects
    createRoom();
    // start a loop that will update the objects' positions
    // and render the scene on each frame
    loop();
}

var controlsEnabled = false;

var moveForward = false;
var moveBackward = false;
var moveLeft = false;
var moveRight = false;
var canJump = false;

var prevTime = performance.now();
var velocity = new THREE.Vector3();

var blocker = document.getElementById( 'world' );
var instructions = document.getElementById( 'world' );

var terrain = [];

var havePointerLock = 'pointerLockElement' in document || 'mozPointerLockElement' in document || 'webkitPointerLockElement' in document;

if ( havePointerLock ) {

    var element = document.getElementById('world');

    var pointerlockchange = function ( event ) {

        if ( document.pointerLockElement === element || document.mozPointerLockElement === element || document.webkitPointerLockElement === element ) {

//          controlsEnabled = true;
            controls.enabled = true;

            //blocker.style.display = 'none';

        } else {

            controls.enabled = false;

            blocker.style.display = '-webkit-box';
            blocker.style.display = '-moz-box';
            blocker.style.display = 'box';

            instructions.style.display = '';

        }

    };

    var pointerlockerror = function ( event ) {

        instructions.style.display = '';

    };

    // Hook pointer lock state change events
    document.addEventListener( 'pointerlockchange', pointerlockchange, false );
    document.addEventListener( 'mozpointerlockchange', pointerlockchange, false );
    document.addEventListener( 'webkitpointerlockchange', pointerlockchange, false );

    document.addEventListener( 'pointerlockerror', pointerlockerror, false );
    document.addEventListener( 'mozpointerlockerror', pointerlockerror, false );
    document.addEventListener( 'webkitpointerlockerror', pointerlockerror, false );

    instructions.addEventListener( 'click', function ( event ) {
        element.requestPointerLock = element.requestPointerLock || element.mozRequestPointerLock || element.webkitRequestPointerLock;
        element.requestPointerLock();

    }, false );

} else {

    instructions.innerHTML = 'Your browser doesn\'t seem to support Pointer Lock API';

}

function createScene() {

    container = document.getElementById('world');
    // Get the width and the height of the screen,
    // use them to set up the aspect ratio of the camera
    // and the size of the renderer.
    HEIGHT = window.innerHeight;
    WIDTH = window.innerWidth;

    // Create the scene
    scene = new THREE.Scene();

    // Add a fog effect to the scene; same color as the
    // background color used in the style sheet
    scene.fog = new THREE.Fog(0xf7d9aa, 100, 950);

    // Create the camera
    aspectRatio = WIDTH / HEIGHT;
    fieldOfView = 60;
    nearPlane = 1;
    farPlane = 10000;
    camera = new THREE.PerspectiveCamera(
        fieldOfView,
        aspectRatio,
        nearPlane,
        farPlane
        );

    // Set the position of the camera
    camera.position.x = 0;
    camera.position.z = 0;
    camera.position.y = 0;

    controls = new THREE.PointerLockControls( camera );
                scene.add( controls.getObject() );

                var onKeyDown = function ( event ) {

                    switch ( event.keyCode ) {

                        case 38: // up
                        case 87: // w
                            moveForward = true;
                            break;

                        case 37: // left
                        case 65: // a
                            moveLeft = true; break;

                        case 40: // down
                        case 83: // s
                            moveBackward = true;
                            break;

                        case 39: // right
                        case 68: // d
                            moveRight = true;
                            break;

                        case 32: // space
                            if ( canJump === true ) velocity.y += 350;
                            canJump = false;
                            break;

                    }

                };

                var onKeyUp = function ( event ) {

                    switch( event.keyCode ) {

                        case 38: // up
                        case 87: // w
                            moveForward = false;
                            break;

                        case 37: // left
                        case 65: // a
                            moveLeft = false;
                            break;

                        case 40: // down
                        case 83: // s
                            moveBackward = false;
                            break;

                        case 39: // right
                        case 68: // d
                            moveRight = false;
                            break;

                    }

                };

    document.addEventListener( 'keydown', onKeyDown, false );
    document.addEventListener( 'keyup', onKeyUp, false );

    raycaster = new THREE.Raycaster( new THREE.Vector3(), new THREE.Vector3( 0, - 1, 0 ), 0, 10 );

    // Create the renderer
    renderer = new THREE.WebGLRenderer({
        // Allow transparency to show the gradient background
        // we defined in the CSS
        alpha: true,

        // Activate the anti-aliasing; this is less performant,
        // but, as our project is low-poly based, it should be fine :)
        antialias: true
    });

    // Define the size of the renderer; in this case,
    // it will fill the entire screen
    renderer.setSize(WIDTH, HEIGHT);

    // Enable shadow rendering
    renderer.shadowMap.enabled = true;

    // Add the DOM element of the renderer to the
    // container we created in the HTML

    container.appendChild(renderer.domElement);

    // stats = new THREE.Stats();
    // container.appendChild( stats.dom );
    // Listen to the screen: if the user resizes it
    // we have to update the camera and the renderer size
    window.addEventListener('resize', handleWindowResize, false);
}

function loop(){

    requestAnimationFrame(loop);

                    raycaster.ray.origin.copy( controls.getObject().position );
                    raycaster.ray.origin.y -= 1;

                    var intersections = raycaster.intersectObjects( terrain );

                    var isOnObject = intersections.length > 0;

                    var time = performance.now();
                    var delta = ( time - prevTime ) / 1000;

                    velocity.x -= velocity.x * 10.0 * delta;
                    velocity.z -= velocity.z * 10.0 * delta;

                    velocity.y -= 9.8 * 100.0 * delta; // 100.0 = mass

                    if ( moveForward ) velocity.z -= 400.0 * delta;
                    if ( moveBackward ) velocity.z += 400.0 * delta;

                    if ( moveLeft ) velocity.x -= 400.0 * delta;
                    if ( moveRight ) velocity.x += 400.0 * delta;

                   if ( isOnObject === true ) {
                       velocity.y = Math.max( 0, velocity.y );

                       canJump = true;
                   }

                    controls.getObject().translateX( velocity.x * delta );
                    controls.getObject().translateY( velocity.y * delta );
                    controls.getObject().translateZ( velocity.z * delta );

                    if ( controls.getObject().position.y < 10 ) {

                        velocity.y = 0;
                        controls.getObject().position.y = 10;

                        canJump = true;

                    }

                    prevTime = time;

    renderer.render(scene, camera);

};

function handleWindowResize() {
    // update height and width of the renderer and the camera
    HEIGHT = window.innerHeight;
    WIDTH = window.innerWidth;
    renderer.setSize(WIDTH, HEIGHT);
    camera.aspect = WIDTH / HEIGHT;
    camera.updateProjectionMatrix();
}

var hemisphereLight, shadowLight;

function createLights() {
    // A hemisphere light is a gradient colored light;
    // the first parameter is the sky color, the second parameter is the ground color,
    // the third parameter is the intensity of the light
    hemisphereLight = new THREE.HemisphereLight(0xaaaaaa,0x000000, .9)

    // A directional light shines from a specific direction.
    // It acts like the sun, that means that all the rays produced are parallel.
    shadowLight = new THREE.DirectionalLight(0xffffff, .9);

    // Set the direction of the light
    shadowLight.position.set(150, 350, 350);

    // Allow shadow casting
    shadowLight.castShadow = true;

    // define the visible area of the projected shadow
    shadowLight.shadow.camera.left = -400;
    shadowLight.shadow.camera.right = 400;
    shadowLight.shadow.camera.top = 400;
    shadowLight.shadow.camera.bottom = -400;
    shadowLight.shadow.camera.near = 1;
    shadowLight.shadow.camera.far = 1000;

    // define the resolution of the shadow; the higher the better,
    // but also the more expensive and less performant
    shadowLight.shadow.mapSize.width = 2048;
    shadowLight.shadow.mapSize.height = 2048;

    // to activate the lights, just add them to the scene
    scene.add(hemisphereLight);
    scene.add(shadowLight);
}

function createRoom() {

    var geomFloor = new THREE.BoxGeometry(200,1,200);
    var geomSide = new THREE.BoxGeometry(1,200,200);
    var geomBack = new THREE.BoxGeometry(200,200,1);
    var material = new THREE.MeshBasicMaterial({color:Colors.red,shading:THREE.FlatShading})
    var floor = new THREE.Mesh(geomFloor, material);

    var leftWall = new THREE.Mesh(geomSide, material);

    var rightWall = new THREE.Mesh(geomSide, material);
    var backWall = new THREE.Mesh(geomBack, material);
    leftWall.position.x -= 100;
    rightWall.position.x +=100;
    backWall.position.z -=100;
    floor.position.y -=100;
    terrain.push(rightWall);
    terrain.push(leftWall);
    terrain.push(backWall);
    terrain.push(floor);

    scene.add(floor);
    scene.add(leftWall);
    scene.add(rightWall);
    scene.add(backWall);

}
<commit_msg>collision with terrain<commit_after>var Colors = {
    red:0xf25346,
    white:0xd8d0d1,
    brown:0x59332e,
    pink:0xF5986E,
    brownDark:0x23190f,
    blue:0x68c3c0,
};
window.addEventListener('load', init, false);

var scene,
camera, fieldOfView, aspectRatio, nearPlane, farPlane, HEIGHT, WIDTH,
renderer, container, controls, clock, raycaster;

function init(event) {

    // set up the scene, the camera and the renderer
    createScene();

    // add the lights
    createLights();

    // add the objects
    createRoom();
    // start a loop that will update the objects' positions
    // and render the scene on each frame
    loop();
}

var controlsEnabled = false;

var moveForward = false;
var moveBackward = false;
var moveLeft = false;
var moveRight = false;
var canJump = false;

var prevTime = performance.now();
var velocity = new THREE.Vector3();

var blocker = document.getElementById( 'world' );
var instructions = document.getElementById( 'world' );

var terrain = [];

var havePointerLock = 'pointerLockElement' in document || 'mozPointerLockElement' in document || 'webkitPointerLockElement' in document;

if ( havePointerLock ) {

    var element = document.getElementById('world');

    var pointerlockchange = function ( event ) {

        if ( document.pointerLockElement === element || document.mozPointerLockElement === element || document.webkitPointerLockElement === element ) {

//          controlsEnabled = true;
            controls.enabled = true;

            //blocker.style.display = 'none';

        } else {

            controls.enabled = false;

            blocker.style.display = '-webkit-box';
            blocker.style.display = '-moz-box';
            blocker.style.display = 'box';

            instructions.style.display = '';

        }

    };

    var pointerlockerror = function ( event ) {

        instructions.style.display = '';

    };

    // Hook pointer lock state change events
    document.addEventListener( 'pointerlockchange', pointerlockchange, false );
    document.addEventListener( 'mozpointerlockchange', pointerlockchange, false );
    document.addEventListener( 'webkitpointerlockchange', pointerlockchange, false );

    document.addEventListener( 'pointerlockerror', pointerlockerror, false );
    document.addEventListener( 'mozpointerlockerror', pointerlockerror, false );
    document.addEventListener( 'webkitpointerlockerror', pointerlockerror, false );

    instructions.addEventListener( 'click', function ( event ) {
        element.requestPointerLock = element.requestPointerLock || element.mozRequestPointerLock || element.webkitRequestPointerLock;
        element.requestPointerLock();

    }, false );

} else {

    instructions.innerHTML = 'Your browser doesn\'t seem to support Pointer Lock API';

}

function createScene() {

    container = document.getElementById('world');
    // Get the width and the height of the screen,
    // use them to set up the aspect ratio of the camera
    // and the size of the renderer.
    HEIGHT = window.innerHeight;
    WIDTH = window.innerWidth;

    // Create the scene
    scene = new THREE.Scene();

    // Add a fog effect to the scene; same color as the
    // background color used in the style sheet
    scene.fog = new THREE.Fog(0xf7d9aa, 100, 950);

    // Create the camera
    aspectRatio = WIDTH / HEIGHT;
    fieldOfView = 60;
    nearPlane = 1;
    farPlane = 10000;
    camera = new THREE.PerspectiveCamera(
        fieldOfView,
        aspectRatio,
        nearPlane,
        farPlane
        );

    // Set the position of the camera
    camera.position.x = 0;
    camera.position.z = 0;
    camera.position.y = 0;

    controls = new THREE.PointerLockControls( camera );
                scene.add( controls.getObject() );

                var onKeyDown = function ( event ) {

                    switch ( event.keyCode ) {

                        case 38: // up
                        case 87: // w
                            moveForward = true;
                            break;

                        case 37: // left
                        case 65: // a
                            moveLeft = true; break;

                        case 40: // down
                        case 83: // s
                            moveBackward = true;
                            break;

                        case 39: // right
                        case 68: // d
                            moveRight = true;
                            break;

                        case 32: // space
                            if ( canJump === true ) velocity.y += 350;
                            canJump = false;
                            break;

                    }

                };

                var onKeyUp = function ( event ) {

                    switch( event.keyCode ) {

                        case 38: // up
                        case 87: // w
                            moveForward = false;
                            break;

                        case 37: // left
                        case 65: // a
                            moveLeft = false;
                            break;

                        case 40: // down
                        case 83: // s
                            moveBackward = false;
                            break;

                        case 39: // right
                        case 68: // d
                            moveRight = false;
                            break;

                    }

                };

    document.addEventListener( 'keydown', onKeyDown, false );
    document.addEventListener( 'keyup', onKeyUp, false );

    raycasterY = new THREE.Raycaster( new THREE.Vector3(), new THREE.Vector3( 0, -1, 0 ), 0, 10 );

    raycasterXpos = new THREE.Raycaster(new THREE.Vector3(), new THREE.Vector3( 1,0 ,0 ), 0, 10 );

    raycasterZpos = new THREE.Raycaster( new THREE.Vector3(), new THREE.Vector3( 0, 0, 1 ), 0, 10 );

    raycasterXneg = new THREE.Raycaster( new THREE.Vector3(), new THREE.Vector3( -1, 0, 0 ), 0, 10 );

    raycasterZneg = new THREE.Raycaster( new THREE.Vector3(), new THREE.Vector3( 0, 0, -1 ), 0, 10 );

    // Create the renderer
    renderer = new THREE.WebGLRenderer({
        // Allow transparency to show the gradient background
        // we defined in the CSS
        alpha: true,

        // Activate the anti-aliasing; this is less performant,
        // but, as our project is low-poly based, it should be fine :)
        antialias: true
    });

    // Define the size of the renderer; in this case,
    // it will fill the entire screen
    renderer.setSize(WIDTH, HEIGHT);

    // Enable shadow rendering
    renderer.shadowMap.enabled = true;

    // Add the DOM element of the renderer to the
    // container we created in the HTML

    container.appendChild(renderer.domElement);

    // stats = new THREE.Stats();
    // container.appendChild( stats.dom );
    // Listen to the screen: if the user resizes it
    // we have to update the camera and the renderer size
    window.addEventListener('resize', handleWindowResize, false);
}

function loop(){

    requestAnimationFrame(loop);

                    raycasterY.ray.origin.copy( controls.getObject().position );
                    raycasterXpos.set( controls.getObject().position, controls.getObject().getWorldDirection().applyAxisAngle( new THREE.Vector3(0,1,0), (Math.PI)/2));
                    raycasterXneg.set( controls.getObject().position, controls.getObject().getWorldDirection().applyAxisAngle( new THREE.Vector3(0,1,0), -(Math.PI)/2)  );
                    raycasterZpos.set( controls.getObject().position, controls.getObject().getWorldDirection() );
                    raycasterZneg.set( controls.getObject().position, controls.getObject().getWorldDirection().negate() );

                    var intersectionsY = raycasterY.intersectObjects( terrain );

                    var intersectionsXpos = raycasterXpos.intersectObjects(terrain);

                    var intersectionsZpos = raycasterZpos.intersectObjects(terrain);;
                    var intersectionsXneg = raycasterXneg.intersectObjects(terrain);;
                    var intersectionsZneg = raycasterZneg.intersectObjects(terrain);;

                    var time = performance.now();
                    var delta = ( time - prevTime ) / 1000;

                    velocity.x -= velocity.x * 10.0 * delta;
                    velocity.z -= velocity.z * 10.0 * delta;

                    velocity.y -= 9.8 * 100.0 * delta; // 100.0 = mass

                    if ( moveForward ) velocity.z -= 400.0 * delta;
                    if ( moveBackward ) velocity.z += 400.0 * delta;

                    if ( moveLeft ) velocity.x -= 400.0 * delta;
                    if ( moveRight ) velocity.x += 400.0 * delta;

                    if (intersectionsY.length > 0) {

                        velocity.y = Math.max( 0, velocity.y );

                        canJump = true;
                    }

                    if(intersectionsZpos.length > 0) {
                        //alert('OBJECT IN BACK');
                        velocity.z = Math.min(0, velocity.z);
                    }

                    if(intersectionsZneg.length > 0) {
                         // alert('OBJECT FRONT');
                        velocity.z = Math.max(0, velocity.z);
                    }

                    if(intersectionsXpos.length > 0) {
                         // alert('OBJECT RIGHT');
                        velocity.x = Math.min(0, velocity.x);
                    }

                    if(intersectionsXneg.length > 0) {
                        // alert('OBJECT LEFT');
                        velocity.x = Math.max(0, velocity.x);
                    }

                    controls.getObject().translateX( velocity.x * delta );
                    controls.getObject().translateY( velocity.y * delta );
                    controls.getObject().translateZ( velocity.z * delta );

                    if ( controls.getObject().position.y < 10 ) {

                        velocity.y = 0;
                        controls.getObject().position.y = 10;

                        canJump = true;

                    }

                    prevTime = time;

    renderer.render(scene, camera);

};

function handleWindowResize() {
    // update height and width of the renderer and the camera
    HEIGHT = window.innerHeight;
    WIDTH = window.innerWidth;
    renderer.setSize(WIDTH, HEIGHT);
    camera.aspect = WIDTH / HEIGHT;
    camera.updateProjectionMatrix();
}

var hemisphereLight, shadowLight;

function createLights() {
    // A hemisphere light is a gradient colored light;
    // the first parameter is the sky color, the second parameter is the ground color,
    // the third parameter is the intensity of the light
    hemisphereLight = new THREE.HemisphereLight(0xaaaaaa,0x000000, .9)

    // A directional light shines from a specific direction.
    // It acts like the sun, that means that all the rays produced are parallel.
    shadowLight = new THREE.DirectionalLight(0xffffff, .9);

    // Set the direction of the light
    shadowLight.position.set(50, 50, 50);

    // Allow shadow casting
    shadowLight.castShadow = true;

    // define the visible area of the projected shadow
    shadowLight.shadow.camera.left = -400;
    shadowLight.shadow.camera.right = 400;
    shadowLight.shadow.camera.top = 400;
    shadowLight.shadow.camera.bottom = -400;
    shadowLight.shadow.camera.near = 1;
    shadowLight.shadow.camera.far = 1000;

    // define the resolution of the shadow; the higher the better,
    // but also the more expensive and less performant
    shadowLight.shadow.mapSize.width = 2048;
    shadowLight.shadow.mapSize.height = 2048;

    // to activate the lights, just add them to the scene
    scene.add(hemisphereLight);
    scene.add(shadowLight);
}

function createRoom() {

    var geomFloor = new THREE.BoxGeometry(200,10,200);
    var geomSide = new THREE.BoxGeometry(10,200,200);
    var geomBack = new THREE.BoxGeometry(200,200,10);
    var material = new THREE.MeshBasicMaterial({color:Colors.red,shading:THREE.FlatShading})
    var floor = new THREE.Mesh(geomFloor, material);

    var leftWall = new THREE.Mesh(geomSide, material);

    var rightWall = new THREE.Mesh(geomSide, material);
    var backWall = new THREE.Mesh(geomBack, material);
    leftWall.position.x -= 100;
    rightWall.position.x +=100;
    backWall.position.z -=100;
    floor.position.y -=100;
    terrain.push(rightWall);
    terrain.push(leftWall);
    terrain.push(backWall);
    terrain.push(floor);

    scene.add(floor);
    scene.add(leftWall);
    scene.add(rightWall);
    scene.add(backWall);

}
<|endoftext|>


<commit_before>(function($){

  $(".layout__slot:nth-child(1)").attr("id","calendar-list");
  $(".layout__slot:nth-child(2)").attr("id","app-list");
  $(".layout__slot:nth-child(3)").attr("id","activity-list");
  $(".layout__slot:nth-child(4)").attr("id","tweets-list");
  $(".layout__slot:nth-child(4)").attr("id","tweets-list");

  var appList = $("#app-list")

  appList.prependTo(appList.parent())
  appList.removeClass();

  var activityList = $("#activity-list")
  activityList.find(".notification li").on("click",function(e){
    var $this = $(this);
    if($this.prop("tagName") == "A"){
      return;
    }
    $(this).find("a").click();
  });

})(jQuery)
<commit_msg>Update page-manipulation.js<commit_after>(function($){

  $(".layout__slot:nth-child(1)").attr("id","calendar-list");
  $(".layout__slot:nth-child(2)").attr("id","app-list");
  $(".layout__slot:nth-child(3)").attr("id","activity-list");
  $(".layout__slot:nth-child(4)").attr("id","tweets-list");
  $(".layout__slot:nth-child(4)").attr("id","tweets-list");

  var appList = $("#app-list")

  appList.prependTo(appList.parent())
  appList.removeClass();

  var activityList = $("#activity-list")
  activityList.find(".notification li").on("click",function(e){
    var $this = $(this);
    if($this.prop("tagName").is("a"){
      return;
    }
    $(this).find("a").click();
  });

})(jQuery)
<|endoftext|>


<commit_before>/* @flow */

import invariant from 'invariant'

import {
  GraphQLObjectType,
  GraphQLInputObjectType,
  GraphQLList,
  GraphQLNonNull,
  GraphQLID,
  isLeafType
} from 'graphql'

import type { GraphQLResolveInfo } from 'graphql'
import { mutationWithClientMutationId } from 'graphql-relay'

import { getSelections } from './request'
import { processSelections } from './response'
import { unwrap, unwrapNonNull, lowerCamelCase } from './utils'

import type { Client } from './client'
import type { Context } from './schema'

const inputTypes = new Map()
function getInputType (
  client: Client,
  type: GraphQLObjectType
): GraphQLInputObjectType {
  const name = type.name + 'Input'

  let inputType = inputTypes.get(name)
  if (inputType) return inputType

  inputType = new GraphQLInputObjectType({
    name: name,
    fields: () => getInputFields(client, type, false)
  })
  inputTypes.set(name, inputType)
  return inputType
}

function getInputFields (
  client: Client,
  type: GraphQLObjectType,
  excludeId: boolean
) {
  const inputFields = {}
  const fields = type.getFields()
  Object.keys(fields).forEach(fieldName => {
    const field = fields[fieldName]
    const fieldType = unwrapNonNull(field.type)
    if (fieldType instanceof GraphQLList) {
      if (fieldType.ofType instanceof GraphQLObjectType) {
        inputFields[fieldName] = {
          type: new GraphQLList(getInputType(client, fieldType.ofType))
        }
      } else {
        inputFields[fieldName] = {
          type: new GraphQLList(fieldType.ofType)
        }
      }
    } else if (fieldType instanceof GraphQLObjectType) {
      inputFields[fieldName] = { type: getInputType(client, fieldType) }
    } else if (isLeafType(fieldType)) {
      if (excludeId && fieldName === 'id') return
      inputFields[fieldName] = { type: fieldType }
    }
  })
  return inputFields
}

export function getDeleteMutation (client: Client, type: GraphQLObjectType) {
  return mutationWithClientMutationId({
    name: `Delete${type.name}Mutation`,
    inputFields: {
      id: { type: new GraphQLNonNull(GraphQLID) }
    },
    outputFields: {
      [lowerCamelCase(type.name)]: {
        type: type
      }
    },
    mutateAndGetPayload: (input, context, info) => {
      return deleteAndGetPayload(client, info, context, type, input)
    }
  })
}

export function getUpdateMutation (client: Client, type: GraphQLObjectType) {
  const name = type.name
  const fields = type.getFields()
  const inputFields = {}
  Object.keys(fields).forEach(fieldName => {
    const field = fields[fieldName]
    const fieldType = unwrap(field.type)
    if (isLeafType(fieldType)) {
      if (fieldName === 'id') {
        inputFields[fieldName] = { type: new GraphQLNonNull(GraphQLID) }
      } else {
        inputFields[fieldName] = { type: fieldType }
      }
    }
  })
  return mutationWithClientMutationId({
    name: `Update${name}Mutation`,
    inputFields,
    outputFields: {
      [lowerCamelCase(name)]: {
        type: type
      }
    },
    mutateAndGetPayload: (input, context, info) => {
      return updateAndGetPayload(client, info, context, type, input)
    }
  })
}

function getMutation (
  client: Client,
  info: GraphQLResolveInfo,
  context: Context
) {
  const mutationType = info.schema.getMutationType()
  invariant(mutationType, 'No mutation type defined in schema')
  const fields = mutationType.getFields()
  const selection = info.operation.selectionSet.selections[0]
  invariant(
    selection.kind === 'Field',
    'Mutation selection must be of kind field'
  )
  const type = fields[selection.name.value].type
  invariant(
    type instanceof GraphQLObjectType,
    'Mutation field type must be GraphQLObjectType'
  )
  invariant(
    selection.selectionSet,
    'Mutation field type must have selectionSet'
  )
  let query = 'query {\n'
  query += getSelections(
    client,
    info,
    context,
    selection.selectionSet.selections,
    type,
    '  ',
    true
  )
  return query + '}'
}

export function getCreateMutation (client: Client, type: GraphQLObjectType) {
  const name = type.name
  const inputFields = getInputFields(client, type, true)
  return mutationWithClientMutationId({
    name: `Create${name}Mutation`,
    inputFields,
    outputFields: {
      [lowerCamelCase(name)]: {
        type: type
      }
    },
    mutateAndGetPayload: (input, context, info) => {
      return createAndGetPayload(client, info, context, type, input)
    }
  })
}

async function deleteAndGetPayload (
  client: Client,
  info: GraphQLResolveInfo,
  context: Context,
  type: GraphQLObjectType,
  input: { id: string }
) {
  const typeName = type.name
  const typeField = lowerCamelCase(typeName)
  let query = 'mutation { delete {'
  query += `  <${input.id}> * * .`
  query += '}}'
  await client.fetchQuery(query)
  return {
    [typeField]: {
      id: input.id
    }
  }
}

async function updateAndGetPayload (
  client: Client,
  info: GraphQLResolveInfo,
  context: Context,
  type: GraphQLObjectType,
  input: { id: string }
) {
  return createOrUpdate(client, info, context, type, input, input.id)
}

function createAndGetPayload (
  client: Client,
  info: GraphQLResolveInfo,
  context: Context,
  type: GraphQLObjectType,
  input: {}
) {
  return createOrUpdate(client, info, context, type, input)
}

function getMutationFields (
  client: Client,
  info: GraphQLResolveInfo,
  context: Context,
  type: GraphQLObjectType,
  input: {},
  ident: string,
  count: number
) {
  let query = ''
  if (ident.indexOf('node') !== -1) {
    query += `  ${ident} <__typename> "${type.name}" .\n`
  }
  console.log(type)
  const fields = type.getFields()
  Object.keys(input).forEach(key => {
    if (key === 'id') return
    let fieldType = fields[key].type
    if (
      fieldType instanceof GraphQLObjectType ||
      fieldType instanceof GraphQLList
    ) {
      let nodes = []
      if (fieldType instanceof GraphQLList) {
        nodes = input[key]
        fieldType = fieldType.ofType
      } else {
        nodes = [input[key]]
      }
      nodes.forEach(node => {
        count++
        let nodeIdent = node.id ? `<${node.id}>` : `_:node${count}`
        let child = getMutationFields(
          client,
          info,
          context,
          fieldType,
          node,
          nodeIdent,
          count
        )
        query = child + query
        query += `  ${ident} <${key}> ${nodeIdent} .\n`
        let reverse = client.getReversePredicate(key)
        if (reverse) {
          query += `  ${nodeIdent} <${reverse}> ${ident} .\n`
        }
      })
    } else {
      const value = client.localizeValue(input[key], key, context.language)
      query += `  ${ident} <${key}> ${value} .\n`
    }
  })
  return query
}

async function createOrUpdate (
  client: Client,
  info: GraphQLResolveInfo,
  context: Context,
  type: GraphQLObjectType,
  input: {},
  id?: string
) {
  const isCreate = typeof id === 'undefined'
  let ident = isCreate ? '_:node' : '<' + String(id) + '>'
  let query = 'mutation { set {\n'
  query += getMutationFields(client, info, context, type, input, ident, 0)
  query += '}}'
  const res = await client.fetchQuery(query)
  query = getMutation(client, info, context)
  // TODO: good god lemmon
  query = query.replace(
    /func:eq\(__typename,[^)]+\)/m,
    'id:' + (id || res.uids.node)
  )
  return client.fetchQuery(query).then(res => {
    const selection = info.operation.selectionSet.selections[0]
    invariant(
      !!selection && selection.kind === 'Field' && selection.selectionSet,
      'Selection not found'
    )
    processSelections(
      client,
      info,
      selection.selectionSet.selections,
      info.schema.getQueryType(),
      res
    )
    return res
  })
}
<commit_msg>Remove log<commit_after>/* @flow */

import invariant from 'invariant'

import {
  GraphQLObjectType,
  GraphQLInputObjectType,
  GraphQLList,
  GraphQLNonNull,
  GraphQLID,
  isLeafType
} from 'graphql'

import type { GraphQLResolveInfo } from 'graphql'
import { mutationWithClientMutationId } from 'graphql-relay'

import { getSelections } from './request'
import { processSelections } from './response'
import { unwrap, unwrapNonNull, lowerCamelCase } from './utils'

import type { Client } from './client'
import type { Context } from './schema'

const inputTypes = new Map()
function getInputType (
  client: Client,
  type: GraphQLObjectType
): GraphQLInputObjectType {
  const name = type.name + 'Input'

  let inputType = inputTypes.get(name)
  if (inputType) return inputType

  inputType = new GraphQLInputObjectType({
    name: name,
    fields: () => getInputFields(client, type, false)
  })
  inputTypes.set(name, inputType)
  return inputType
}

function getInputFields (
  client: Client,
  type: GraphQLObjectType,
  excludeId: boolean
) {
  const inputFields = {}
  const fields = type.getFields()
  Object.keys(fields).forEach(fieldName => {
    const field = fields[fieldName]
    const fieldType = unwrapNonNull(field.type)
    if (fieldType instanceof GraphQLList) {
      if (fieldType.ofType instanceof GraphQLObjectType) {
        inputFields[fieldName] = {
          type: new GraphQLList(getInputType(client, fieldType.ofType))
        }
      } else {
        inputFields[fieldName] = {
          type: new GraphQLList(fieldType.ofType)
        }
      }
    } else if (fieldType instanceof GraphQLObjectType) {
      inputFields[fieldName] = { type: getInputType(client, fieldType) }
    } else if (isLeafType(fieldType)) {
      if (excludeId && fieldName === 'id') return
      inputFields[fieldName] = { type: fieldType }
    }
  })
  return inputFields
}

export function getDeleteMutation (client: Client, type: GraphQLObjectType) {
  return mutationWithClientMutationId({
    name: `Delete${type.name}Mutation`,
    inputFields: {
      id: { type: new GraphQLNonNull(GraphQLID) }
    },
    outputFields: {
      [lowerCamelCase(type.name)]: {
        type: type
      }
    },
    mutateAndGetPayload: (input, context, info) => {
      return deleteAndGetPayload(client, info, context, type, input)
    }
  })
}

export function getUpdateMutation (client: Client, type: GraphQLObjectType) {
  const name = type.name
  const fields = type.getFields()
  const inputFields = {}
  Object.keys(fields).forEach(fieldName => {
    const field = fields[fieldName]
    const fieldType = unwrap(field.type)
    if (isLeafType(fieldType)) {
      if (fieldName === 'id') {
        inputFields[fieldName] = { type: new GraphQLNonNull(GraphQLID) }
      } else {
        inputFields[fieldName] = { type: fieldType }
      }
    }
  })
  return mutationWithClientMutationId({
    name: `Update${name}Mutation`,
    inputFields,
    outputFields: {
      [lowerCamelCase(name)]: {
        type: type
      }
    },
    mutateAndGetPayload: (input, context, info) => {
      return updateAndGetPayload(client, info, context, type, input)
    }
  })
}

function getMutation (
  client: Client,
  info: GraphQLResolveInfo,
  context: Context
) {
  const mutationType = info.schema.getMutationType()
  invariant(mutationType, 'No mutation type defined in schema')
  const fields = mutationType.getFields()
  const selection = info.operation.selectionSet.selections[0]
  invariant(
    selection.kind === 'Field',
    'Mutation selection must be of kind field'
  )
  const type = fields[selection.name.value].type
  invariant(
    type instanceof GraphQLObjectType,
    'Mutation field type must be GraphQLObjectType'
  )
  invariant(
    selection.selectionSet,
    'Mutation field type must have selectionSet'
  )
  let query = 'query {\n'
  query += getSelections(
    client,
    info,
    context,
    selection.selectionSet.selections,
    type,
    '  ',
    true
  )
  return query + '}'
}

export function getCreateMutation (client: Client, type: GraphQLObjectType) {
  const name = type.name
  const inputFields = getInputFields(client, type, true)
  return mutationWithClientMutationId({
    name: `Create${name}Mutation`,
    inputFields,
    outputFields: {
      [lowerCamelCase(name)]: {
        type: type
      }
    },
    mutateAndGetPayload: (input, context, info) => {
      return createAndGetPayload(client, info, context, type, input)
    }
  })
}

async function deleteAndGetPayload (
  client: Client,
  info: GraphQLResolveInfo,
  context: Context,
  type: GraphQLObjectType,
  input: { id: string }
) {
  const typeName = type.name
  const typeField = lowerCamelCase(typeName)
  let query = 'mutation { delete {'
  query += `  <${input.id}> * * .`
  query += '}}'
  await client.fetchQuery(query)
  return {
    [typeField]: {
      id: input.id
    }
  }
}

async function updateAndGetPayload (
  client: Client,
  info: GraphQLResolveInfo,
  context: Context,
  type: GraphQLObjectType,
  input: { id: string }
) {
  return createOrUpdate(client, info, context, type, input, input.id)
}

function createAndGetPayload (
  client: Client,
  info: GraphQLResolveInfo,
  context: Context,
  type: GraphQLObjectType,
  input: {}
) {
  return createOrUpdate(client, info, context, type, input)
}

function getMutationFields (
  client: Client,
  info: GraphQLResolveInfo,
  context: Context,
  type: GraphQLObjectType,
  input: {},
  ident: string,
  count: number
) {
  let query = ''
  if (ident.indexOf('node') !== -1) {
    query += `  ${ident} <__typename> "${type.name}" .\n`
  }
  const fields = type.getFields()
  Object.keys(input).forEach(key => {
    if (key === 'id') return
    let fieldType = fields[key].type
    if (
      fieldType instanceof GraphQLObjectType ||
      fieldType instanceof GraphQLList
    ) {
      let nodes = []
      if (fieldType instanceof GraphQLList) {
        nodes = input[key]
        fieldType = fieldType.ofType
      } else {
        nodes = [input[key]]
      }
      nodes.forEach(node => {
        count++
        let nodeIdent = node.id ? `<${node.id}>` : `_:node${count}`
        let child = getMutationFields(
          client,
          info,
          context,
          fieldType,
          node,
          nodeIdent,
          count
        )
        query = child + query
        query += `  ${ident} <${key}> ${nodeIdent} .\n`
        let reverse = client.getReversePredicate(key)
        if (reverse) {
          query += `  ${nodeIdent} <${reverse}> ${ident} .\n`
        }
      })
    } else {
      const value = client.localizeValue(input[key], key, context.language)
      query += `  ${ident} <${key}> ${value} .\n`
    }
  })
  return query
}

async function createOrUpdate (
  client: Client,
  info: GraphQLResolveInfo,
  context: Context,
  type: GraphQLObjectType,
  input: {},
  id?: string
) {
  const isCreate = typeof id === 'undefined'
  let ident = isCreate ? '_:node' : '<' + String(id) + '>'
  let query = 'mutation { set {\n'
  query += getMutationFields(client, info, context, type, input, ident, 0)
  query += '}}'
  const res = await client.fetchQuery(query)
  query = getMutation(client, info, context)
  // TODO: good god lemmon
  query = query.replace(
    /func:eq\(__typename,[^)]+\)/m,
    'id:' + (id || res.uids.node)
  )
  return client.fetchQuery(query).then(res => {
    const selection = info.operation.selectionSet.selections[0]
    invariant(
      !!selection && selection.kind === 'Field' && selection.selectionSet,
      'Selection not found'
    )
    processSelections(
      client,
      info,
      selection.selectionSet.selections,
      info.schema.getQueryType(),
      res
    )
    return res
  })
}
<|endoftext|>