<commit_before>/*** @using { core.RAIIEventEmitter } **/

var Color = _globals.core.Color

var Rect = function(l, t, r, b) {
	this.l = l || 0
	this.t = t || 0
	this.r = r || 0
	this.b = b || 0
}

Rect.prototype.constructor = Rect
Rect.prototype.toString = function() {
	return '[' + this.l + ', ' + this.t + ', ' + this.r + ', ' + this.b + ']'
}

Rect.prototype.valid = function() {
	return this.b > this.t && this.r > this.l
}

Rect.prototype.width = function() { return this.r - this.l }
Rect.prototype.height = function() { return this.b - this.t }

Rect.prototype.move = function(dx, dy) {
	this.l += dx
	this.t += dy
	this.r += dx
	this.b += dy
}

Rect.prototype.moved = function(dx, dy) {
	return new Rect(this.l + dx, this.t + dy, this.r + dx, this.b + dy)
}

Rect.prototype.clone = function() {
	return new Rect(this.l, this.t, this.r, this.b)
}

Rect.prototype.union = function(rect) {
	if (!this.valid())
		return rect.clone()
	else if (!rect.valid())
		return this.clone()
	else
		return new Rect(
			Math.min(this.l, rect.l),
			Math.min(this.t, rect.t),
			Math.max(this.r, rect.r),
			Math.max(this.b, rect.b)
	)
}

Rect.prototype.intersect = function(rect) {
	if (!this.valid())
		return rect.clone()
	else if (!rect.valid())
		return this.clone()
	else
		return new Rect(
			Math.max(this.l, rect.l),
			Math.max(this.t, rect.t),
			Math.min(this.r, rect.r),
			Math.min(this.b, rect.b)
		)
}

var registerGenericListener = function(target) {
	var prefix = '__nativeEventHandler_'
	target.onListener('',
		function(name) {
			log('registering generic event', name)
			var pname = prefix + name
			var callback = target[pname] = function() {
				COPY_ARGS(args, 0)
				target.emitWithArgs(name, args)
			}
			//target.dom.addEventListener(name, callback)
		},
		function(name) {
			log('removing generic event', name)
			var pname = prefix + name
			//target.dom.removeEventListener(name, target[pname])
		}
	)
}

var Element = function(context, tag) {
	_globals.core.RAIIEventEmitter.apply(this)
	this._context = context
	this._styles = {}
	this.children = []
	this.dirty = new Rect()
	this._updated = false
	registerGenericListener(this)
}

Element.prototype = Object.create(_globals.core.RAIIEventEmitter.prototype)
Element.prototype.constructor = Element

Element.prototype.addClass = function(cls) { }

var importantStylesList = [
	'left', 'top', 'width', 'height', 'visibility',
	'background-color', 'background-image',
	'border-radius',
	'color', 'font-size', 'text-align'
]
var importantStyles = {}
importantStylesList.forEach(function(name) { importantStyles[name] = true })

Element.prototype._onUpdate = function(name) {
	if (importantStyles[name])
		this.update()
//	else
//		log('unhandled style ' + name)
}

Element.prototype.style = function(name, style) {
	if (style !== undefined) {
		this._onUpdate(name)
		if (style !== '') //fixme: replace it with explicit 'undefined' syntax
			this._styles[name] = style
		else
			delete this._styles[name]
	} else if (name instanceof Object) { //style({ }) assignment
		for(var k in name) {
			this._onUpdate(k)
			var value = name[k]
			if (value !== '') //fixme: replace it with explicit 'undefined' syntax
				this._styles[k] = value
			else
				delete this._styles[k]
		}
	}
	else
		return this._styles[name]
}

Element.prototype.updateStyle = function() { }

Element.prototype.visible = function() {
	var visibility = this._styles['visibility']
	return visibility !== 'hidden'
}

var updateTimer

var renderFrame = function(ctx, renderer) {
	if (updateTimer === undefined) {
		updateTimer = setTimeout(function() {
			updateTimer = undefined
			var dirty = new Rect()
			ctx._updatedItems.forEach(function(el) {
				dirty = dirty.union(el.dirty)
				if (el.visible())
					dirty = dirty.union(el.getScreenRect())
				el._updated = false
			})
			ctx._updatedItems = []
			console.log('painting frame with rect', dirty)
			ctx.renderer.setClip(dirty)
			ctx.element.paint(ctx.renderer, 0, 0)
			//ctx.renderer.fillRect(dirty, new Color('#ff00ff80')) //debug minimal update
		}, 0)
	}
}
exports.renderFrame = renderFrame

Element.prototype.update = function() {
	if (this._updated || (!this.visible() && !this.dirty.valid()))
		return

	var ctx = this._context
	ctx._updatedItems.push(this)
	this._updated = true //scheduled for this update

	if (!ctx.renderer || !ctx._completed)
		return

	renderFrame(ctx)
}

Element.prototype.append = function(child) {
	if (child._parent !== undefined)
		throw new Error('double append on element')
	child._parent = this
	this.children.push(child)
	child.update()
}

Element.prototype.remove = function() {
	var parent = this._parent
	if (parent !== undefined) {
		var idx = parent.children.indexOf(this)
		if (idx < 0)
			throw new Error('remove(): no child in parent children array')
		parent.children.splice(idx, 1)
		parent.update()
		this._parent = undefined
	} else
		throw new Error('remove() called without adding to parent')
}

Element.prototype.getRect = function() {
	var style = this._styles
	var l = style['left'] || 0, t = style['top'] || 0
	var w = style['width'] || 0, h = style['height'] || 0
	return new Rect(l, t, w + l, h + t)
}

Element.prototype.getScreenRect = function() {
	var rect = this.getRect()
	var el = this._parent
	while(el !== undefined) {
		var style = el._styles
		var l = style['left'] || 0, t = style['top'] || 0
		rect.move(l, t)
		el = el._parent
	}
	return rect
}

Element.prototype.paint = function(renderer, x, y) {
	var visibility = this._styles['visibility']
	if (visibility === 'hidden')
		return new Rect()

	var rect = this.getRect()
	rect.move(x, y)
	var dirty = rect.clone()

	var color = this._styles['background-color']
	if (color !== undefined) {
		color = new Color(color)
		renderer.fillRect(rect, color, this)
	}

	if (this._image !== undefined) {
		renderer.drawImage(rect, this._image, this)
	}

	//render here
	this.children.forEach(function(child) {
		dirty = dirty.union(child.paint(renderer, rect.l, rect.t))
	})
	this.dirty = dirty
	return dirty
}

exports.Rect = Rect
exports.Element = Element
<commit_msg>modify rects in place, instead of creating new one<commit_after>/*** @using { core.RAIIEventEmitter } **/

var Color = _globals.core.Color

var Rect = function(l, t, r, b) {
	this.l = l || 0
	this.t = t || 0
	this.r = r || 0
	this.b = b || 0
}

Rect.prototype.constructor = Rect
Rect.prototype.toString = function() {
	return '[' + this.l + ', ' + this.t + ', ' + this.r + ', ' + this.b + ']'
}

Rect.prototype.valid = function() {
	return this.b > this.t && this.r > this.l
}

Rect.prototype.width = function() { return this.r - this.l }
Rect.prototype.height = function() { return this.b - this.t }

Rect.prototype.move = function(dx, dy) {
	this.l += dx
	this.t += dy
	this.r += dx
	this.b += dy
}

Rect.prototype.moved = function(dx, dy) {
	return new Rect(this.l + dx, this.t + dy, this.r + dx, this.b + dy)
}

Rect.prototype.clone = function() {
	return new Rect(this.l, this.t, this.r, this.b)
}

Rect.prototype.union = function(rect) {
	if (!rect.valid())
		return
	else if (!this.valid()) {
		Object.assign(this, rect)
	} else {
		this.l = Math.min(this.l, rect.l)
		this.t = Math.min(this.t, rect.t)
		this.r = Math.max(this.r, rect.r)
		this.b = Math.max(this.b, rect.b)
	}
}

Rect.prototype.unified = function(rect) {
	if (!this.valid())
		return rect.clone()
	else if (!rect.valid())
		return this.clone()
	else
		return new Rect(
			Math.min(this.l, rect.l),
			Math.min(this.t, rect.t),
			Math.max(this.r, rect.r),
			Math.max(this.b, rect.b)
	)
}

Rect.prototype.intersected = function(rect) {
	if (!this.valid())
		return rect.clone()
	else if (!rect.valid())
		return this.clone()
	else
		return new Rect(
			Math.max(this.l, rect.l),
			Math.max(this.t, rect.t),
			Math.min(this.r, rect.r),
			Math.min(this.b, rect.b)
		)
}

var registerGenericListener = function(target) {
	var prefix = '__nativeEventHandler_'
	target.onListener('',
		function(name) {
			log('registering generic event', name)
			var pname = prefix + name
			var callback = target[pname] = function() {
				COPY_ARGS(args, 0)
				target.emitWithArgs(name, args)
			}
			//target.dom.addEventListener(name, callback)
		},
		function(name) {
			log('removing generic event', name)
			var pname = prefix + name
			//target.dom.removeEventListener(name, target[pname])
		}
	)
}

var Element = function(context, tag) {
	_globals.core.RAIIEventEmitter.apply(this)
	this._context = context
	this._styles = {}
	this.children = []
	this.dirty = new Rect()
	this._updated = false
	registerGenericListener(this)
}

Element.prototype = Object.create(_globals.core.RAIIEventEmitter.prototype)
Element.prototype.constructor = Element

Element.prototype.addClass = function(cls) { }

var importantStylesList = [
	'left', 'top', 'width', 'height', 'visibility',
	'background-color', 'background-image',
	'border-radius',
	'color', 'font-size', 'text-align'
]
var importantStyles = {}
importantStylesList.forEach(function(name) { importantStyles[name] = true })

Element.prototype._onUpdate = function(name) {
	if (importantStyles[name])
		this.update()
//	else
//		log('unhandled style ' + name)
}

Element.prototype.style = function(name, style) {
	if (style !== undefined) {
		this._onUpdate(name)
		if (style !== '') //fixme: replace it with explicit 'undefined' syntax
			this._styles[name] = style
		else
			delete this._styles[name]
	} else if (name instanceof Object) { //style({ }) assignment
		for(var k in name) {
			this._onUpdate(k)
			var value = name[k]
			if (value !== '') //fixme: replace it with explicit 'undefined' syntax
				this._styles[k] = value
			else
				delete this._styles[k]
		}
	}
	else
		return this._styles[name]
}

Element.prototype.updateStyle = function() { }

Element.prototype.visible = function() {
	var visibility = this._styles['visibility']
	return visibility !== 'hidden'
}

var updateTimer

var renderFrame = function(ctx, renderer) {
	if (updateTimer === undefined) {
		updateTimer = setTimeout(function() {
			updateTimer = undefined
			var dirty = new Rect()
			ctx._updatedItems.forEach(function(el) {
				dirty.union(el.dirty)
				if (el.visible())
					dirty.union(el.getScreenRect())
				el._updated = false
			})
			ctx._updatedItems = []
			console.log('painting frame with rect', dirty)
			ctx.renderer.setClip(dirty)
			ctx.element.paint(ctx.renderer, 0, 0)
			//ctx.renderer.fillRect(dirty, new Color('#ff00ff80')) //debug minimal update
		}, 0)
	}
}
exports.renderFrame = renderFrame

Element.prototype.update = function() {
	if (this._updated || (!this.visible() && !this.dirty.valid()))
		return

	var ctx = this._context
	ctx._updatedItems.push(this)
	this._updated = true //scheduled for this update

	if (!ctx.renderer || !ctx._completed)
		return

	renderFrame(ctx)
}

Element.prototype.append = function(child) {
	if (child._parent !== undefined)
		throw new Error('double append on element')
	child._parent = this
	this.children.push(child)
	child.update()
}

Element.prototype.remove = function() {
	var parent = this._parent
	if (parent !== undefined) {
		var idx = parent.children.indexOf(this)
		if (idx < 0)
			throw new Error('remove(): no child in parent children array')
		parent.children.splice(idx, 1)
		parent.update()
		this._parent = undefined
	} else
		throw new Error('remove() called without adding to parent')
}

Element.prototype.getRect = function() {
	var style = this._styles
	var l = style['left'] || 0, t = style['top'] || 0
	var w = style['width'] || 0, h = style['height'] || 0
	return new Rect(l, t, w + l, h + t)
}

Element.prototype.getScreenRect = function() {
	var rect = this.getRect()
	var el = this._parent
	while(el !== undefined) {
		var style = el._styles
		var l = style['left'] || 0, t = style['top'] || 0
		rect.move(l, t)
		el = el._parent
	}
	return rect
}

Element.prototype.paint = function(renderer, x, y) {
	var visibility = this._styles['visibility']
	if (visibility === 'hidden')
		return new Rect()

	var rect = this.getRect()
	rect.move(x, y)
	var dirty = rect.clone()

	var color = this._styles['background-color']
	if (color !== undefined) {
		color = new Color(color)
		renderer.fillRect(rect, color, this)
	}

	if (this._image !== undefined) {
		renderer.drawImage(rect, this._image, this)
	}

	//render here
	this.children.forEach(function(child) {
		dirty.union(child.paint(renderer, rect.l, rect.t))
	})
	this.dirty = dirty
	return dirty
}

exports.Rect = Rect
exports.Element = Element
<|endoftext|>


<commit_before>/**
 * Canvas rendering support for MapController.
 **/

/* Copyright (C) 2012, Code for America
 * This is open source software, released under a standard 2-clause
 * BSD-style license; see the file LICENSE for details.
 */

MapController.CanvasRenderer = {
  // ---------------------- RENDERER METHODS -------------------------

  _initializeRenderer: function () {
    // adjust for an accuracy vs. drawing speed sweet spot (more accuracy makes drawing markers slower)
    // 1 == perfect accuracy, 2 == every other pixel, etc. Integers only, please!
    this.interactionResolution = 2;
    this._featureMap = {};
    this._initializeIcons();
    this._allRequests = [];
    this._markerPoolSize = Config.maxMarkers || 500;
  },

  _initializeMapRenderer: function () {
    this.canvasTiles = new L.TileLayer.Canvas();

    var self = this;
    this.canvasTiles.drawTile = function() { self.drawTile.apply(self, arguments); };

    this.map.addLayer(this.canvasTiles);

    // events
    this.map.on("click", this.handleEvent, this);
    // map has no mousemove events; only drag-related events ("move" is dragging)
    this.map._container.addEventListener("mousemove", this, false);
  },

  _updateRenderer: function () {

    // honor max marker limit
    if (this._allRequests.length >= this._markerPoolSize)
      return;

    // Join and sort latitudinally
    this._allRequests = this._closedRequests.concat(this._openedRequests, this._openRequests).sort(function (a, b) {
      var latitudeDiff = b.lat - a.lat;
      if (latitudeDiff) {
        return latitudeDiff;
      }
      else if (a.statusType === "closed" || a.statusType === "opened" && b.statusType !== "closed") {
        return 1;
      }
      else {
        return -1;
      }
    });

    // Wait for icons to be ready for rendering
    if (!this._iconsReady()) {
      this._waitingToUpdate = true;
      return;
    }

    // var start = Date.now();
    this.canvasTiles.redraw();
    // var time = Date.now() - start;
    // this.renderInfo = this.renderInfo || { renders: 0, total: 0, totalPer: 0, records: []};
    // this.renderInfo.renders += 1;
    // this.renderInfo.total += time;
    // this.renderInfo.totalPer += time / this._allRequests.length;
    // console.log("AVERAGE RENDER TIME PER FEATURE: ", this.renderInfo.totalPer / this.renderInfo.renders);
  },

  _handleEventRenderer: function (event) {
    if (event.target.nodeName === "IMG") {
      this._handleIconImageLoad(event);
    }
    else if (event.type === "mousemove") {
      // FIXME: use map drag events; don't look into private stuff that might change
      var dragging = this.map.dragging._draggable._moving;
      if (!dragging) {
        // FIXME: getBoundingClientRect() will explode horribly in many places
        var mapBounds = this.map._container.getBoundingClientRect();
        var layerPoint = new L.Point(event.pageX - mapBounds.left, event.pageY - mapBounds.top);
        var mapPoint = this.map._initialTopLeftPoint.add(layerPoint);
        var feature = this._getFeatureAtPoint(mapPoint);
        this.map._container.style.cursor = feature ? "pointer" : "default";
      }

    }
    else if (event.type === "click") {
      // console.log(event.layerPoint.x, event.layerPoint.y);
      // console.log(this.map._initialTopLeftPoint.x, this.map._initialTopLeftPoint.y, " / ", this.map._initialTopLeftPoint.x / 256, this.map._initialTopLeftPoint.y / 256);

      var mapPoint = this.map._initialTopLeftPoint.add(event.layerPoint);
      var tile = new L.Point(Math.floor(mapPoint.x / 256), Math.floor(mapPoint.y / 256));
      var position = new L.Point(mapPoint.x % 256, mapPoint.y % 256);
      // console.log("Tile: " + tile + ", point: " + position);
      var feature = this._getFeatureAtPoint(mapPoint);
      console.log(feature);

      if (feature) {
        // create a standard popup
        // TODO: make it nicer.
        var popup = new L.Popup({
          offset: new L.Point(0, -41)
        });
        popup.setLatLng(new L.LatLng(feature.lat, feature.long));
        popup.setContent(this.popupForRequest(feature));
        this.map.openPopup(popup);
      }
    }
  },

  // ---------------------- DRAWING -------------------------

  drawTile: function (canvas, tilePoint, zoom) {
    var ctx = canvas.getContext('2d');
    ctx.clearRect(0, 0, 256, 256);

    // don't do anything if we don't have requests to draw
    if (this._allRequests.length === 0) {
      return;
    }

    var showTypes = {
      open: this.dataSource.filterConditions.states.indexOf("open") > -1,
      opened: this.dataSource.filterConditions.states.indexOf("opened") > -1,
      closed: this.dataSource.filterConditions.states.indexOf("closed") > -1
    };

    var tilePixelPoint = tilePoint.multiplyBy(256);
    var tileKey = tilePoint.toString();
    this._createEmptyFeatureTile(tileKey);
    this._allRequests.forEach(function (request, index) {
      if (showTypes[request.statusType]) {
        var icon = this.icons[request.statusType];
        var point = this.map.project(new L.LatLng(request.lat, request.long))._round()._subtract(tilePixelPoint);
        if (point.x > -15 && point.x < 270 && point.y > -41 && point.y < 296) {
          ctx.drawImage(icon.image, point.x + icon.offset.x, point.y + icon.offset.y);
          this._setFeatureAtPoint(tileKey, point, icon, request, index);
        }
      }
    }, this);
  },

  // ---------------------- FEATURE INTERACTION MANAGEMENT -------------------------

  _createEmptyFeatureTile: function (tilePoint) {
    this._featureMap[tilePoint] = {};
  },

  _setFeatureAtPoint: function (tilePoint, featurePoint, icon, feature, featureIndex) {
    // WARNING: This method is HOT. Measure performance thoroughly before making changes.

    // No need to do any checks here; _createEmptyFeatureTile() must always be run first
    var featureMap = this._featureMap[tilePoint];

    var resolution = this.interactionResolution || 1;

    var offsetX = featurePoint.x + icon.offset.x;
    var offsetY = featurePoint.y + icon.offset.y;
    // rectify offsets to interaction resolution
    offsetX = offsetX - (offsetX % resolution);
    offsetY = offsetY - (offsetY % resolution);
    var mask = icon.mask;
    // speediest looping
    var i = mask.length;
    while (i-- > 0) {
      var point = mask[i];
      var x = point.x + offsetX;
      var y = point.y + offsetY;
      // a tree structure for the map is very slow
      // making a key via (x + "," + y) is faster
      // a single number is fastest! (in V8)
      featureMap[y * 256 + x] = feature;
    }
  },

  _getFeatureAtPoint: function (tilePoint, featurePoint) {
    if (!featurePoint) {
      var point = tilePoint;
      tilePoint = new L.Point(Math.floor(point.x / 256), Math.floor(point.y / 256));
      featurePoint = new L.Point(point.x % 256, point.y % 256);
    }

    if (!this._featureMap) {
      this._featureMap = {};
    }

    var feature = null;
    var tile = this._featureMap[tilePoint];
    if (tile) {
      var resolution = this.interactionResolution || 1;
      var x = featurePoint.x - (featurePoint.x % resolution);
      var y = featurePoint.y - (featurePoint.y % resolution);
      feature = tile[y * 256 + x];
    }
    return feature;
  },

  // ---------------------- ICON MANAGEMENT -------------------------

  _initializeIcons: function () {
    this.icons = {
      open:   this._createIconImage(MapController.ICON_PATHS.default),
      opened: this._createIconImage(MapController.ICON_PATHS.opened),
      closed: this._createIconImage(MapController.ICON_PATHS.closed),
    }
  },

  _createIconImage: function (path) {
    var image = new Image();
    image.src = path;

    var info = {
      image: image,
      offset: {x: 0, y: 0},
      mask: []
    }
    image.representedObject = info;

    if (image.complete) {
      this._setupIcon(info, 2);
    }
    else {
      image.addEventListener("load", this, false);
      image.addEventListener("error", this, false);
    }

    return info;
  },

  _handleIconImageLoad: function (event) {
    event.target.removeEventListener("load", this, false);
    event.target.removeEventListener("error", this, false);
    // TODO: handle load failure
    if (event.type === "load") {
      this._setupIcon(event.target.representedObject);
    }

    if (this._iconsReady() && this._waitingToUpdate) {
      this.update();
    }
  },

  _setupIcon: function (icon) {
    icon.offset.x = -Math.floor(icon.image.width / 2);
    icon.offset.y = 1 - icon.image.height;

    // make the interaction mask
    var workCanvas = document.createElement("canvas");
    workCanvas.width = icon.image.width;
    workCanvas.height = icon.image.height;
    var workCtx = workCanvas.getContext("2d");
    workCtx.drawImage(icon.image, 0, 0);
    var iconData = workCtx.getImageData(0, 0, icon.image.width, icon.image.height).data;
    var rowLength = icon.image.width;

    var resolution = this.interactionResolution || 1;
    for (var i = 3, len = iconData.length; i < len; i += 4) {
      if (iconData[i] > 128) {
        var pixel = (i - 3) / 4;
        var y = Math.floor(pixel / rowLength);
        var x = pixel % rowLength;
        if (y % resolution === 0 && x % resolution === 0) {
          // note: an array of points is *much* faster than a tree structure to iterate through
          // could potentially save memory by saving a single number (256 * y + x)
          icon.mask.push({x: x, y: y});
        }
      }
    }
  },

  _iconsReady: function () {
    return this.icons.open.image.complete && this.icons.opened.image.complete && this.icons.closed.image.complete;
  },
};

<commit_msg>added popup event handling and state mgmt to close popup if marker that generated popup is pressed 2nd time<commit_after>/**
 * Canvas rendering support for MapController.
 **/

/* Copyright (C) 2012, Code for America
 * This is open source software, released under a standard 2-clause
 * BSD-style license; see the file LICENSE for details.
 */

MapController.CanvasRenderer = {
  // ---------------------- RENDERER METHODS -------------------------

  _initializeRenderer: function () {
    // adjust for an accuracy vs. drawing speed sweet spot (more accuracy makes drawing markers slower)
    // 1 == perfect accuracy, 2 == every other pixel, etc. Integers only, please!
    this.interactionResolution = 2;
    this._featureMap = {};
    this._initializeIcons();
    this._allRequests = [];
    this._markerPoolSize = Config.maxMarkers || 500;
    this._currentPopup = null;
  },

  _initializeMapRenderer: function () {
    var self = this;
    this.canvasTiles = new L.TileLayer.Canvas();

    // capture open popup as state saving mechanism so we can have
    // custom behavior of closing the open popup (and not opening
    // new popup) if user clicks on marker a second time
    this.map.on("popupopen", function(e){
      self._currentPopup = e.popup;
    });

    this.canvasTiles.drawTile = function() { self.drawTile.apply(self, arguments); };

    this.map.addLayer(this.canvasTiles);

    // events
    this.map.on("click", this.handleEvent, this);
    // map has no mousemove events; only drag-related events ("move" is dragging)
    this.map._container.addEventListener("mousemove", this, false);
  },

  _updateRenderer: function () {

    // honor max marker limit
    if (this._allRequests.length >= this._markerPoolSize)
      return;

    // Join and sort latitudinally
    this._allRequests = this._closedRequests.concat(this._openedRequests, this._openRequests).sort(function (a, b) {
      var latitudeDiff = b.lat - a.lat;
      if (latitudeDiff) {
        return latitudeDiff;
      }
      else if (a.statusType === "closed" || a.statusType === "opened" && b.statusType !== "closed") {
        return 1;
      }
      else {
        return -1;
      }
    });

    // Wait for icons to be ready for rendering
    if (!this._iconsReady()) {
      this._waitingToUpdate = true;
      return;
    }

    // var start = Date.now();
    this.canvasTiles.redraw();
    // var time = Date.now() - start;
    // this.renderInfo = this.renderInfo || { renders: 0, total: 0, totalPer: 0, records: []};
    // this.renderInfo.renders += 1;
    // this.renderInfo.total += time;
    // this.renderInfo.totalPer += time / this._allRequests.length;
    // console.log("AVERAGE RENDER TIME PER FEATURE: ", this.renderInfo.totalPer / this.renderInfo.renders);
  },

  _handleEventRenderer: function (event) {
    if (event.target.nodeName === "IMG") {
      this._handleIconImageLoad(event);
    }
    else if (event.type === "mousemove") {
      // FIXME: use map drag events; don't look into private stuff that might change
      var dragging = this.map.dragging._draggable._moving;
      if (!dragging) {
        // FIXME: getBoundingClientRect() will explode horribly in many places
        var mapBounds = this.map._container.getBoundingClientRect();
        var layerPoint = new L.Point(event.pageX - mapBounds.left, event.pageY - mapBounds.top);
        var mapPoint = this.map._initialTopLeftPoint.add(layerPoint);
        var feature = this._getFeatureAtPoint(mapPoint);
        this.map._container.style.cursor = feature ? "pointer" : "default";
      }

    }
    else if (event.type === "click") {
      // console.log(event.layerPoint.x, event.layerPoint.y);
      // console.log(this.map._initialTopLeftPoint.x, this.map._initialTopLeftPoint.y, " / ", this.map._initialTopLeftPoint.x / 256, this.map._initialTopLeftPoint.y / 256);

      var mapPoint = this.map._initialTopLeftPoint.add(event.layerPoint);
      var tile = new L.Point(Math.floor(mapPoint.x / 256), Math.floor(mapPoint.y / 256));
      var position = new L.Point(mapPoint.x % 256, mapPoint.y % 256);

      var feature = this._getFeatureAtPoint(mapPoint);

      if (feature) {
        // create a standard popup
        // TODO: make it nicer.
        var popup = new L.Popup({
          offset: new L.Point(0, -41)
        });
        popup.setLatLng(new L.LatLng(feature.lat, feature.long));
        popup.setContent(this.popupForRequest(feature));
        // don't bother creating popup if one is already open
        // and the new popup would be the same
        if (this._currentPopup && this._currentPopup._content === popup._content) {
          this._currentPopup = null;
          return;
        }
        this.map.openPopup(popup);
      }
    }
  },

  // ---------------------- DRAWING -------------------------

  drawTile: function (canvas, tilePoint, zoom) {
    var ctx = canvas.getContext('2d');
    ctx.clearRect(0, 0, 256, 256);

    // don't do anything if we don't have requests to draw
    if (this._allRequests.length === 0) {
      return;
    }

    var showTypes = {
      open: this.dataSource.filterConditions.states.indexOf("open") > -1,
      opened: this.dataSource.filterConditions.states.indexOf("opened") > -1,
      closed: this.dataSource.filterConditions.states.indexOf("closed") > -1
    };

    var tilePixelPoint = tilePoint.multiplyBy(256);
    var tileKey = tilePoint.toString();
    this._createEmptyFeatureTile(tileKey);
    this._allRequests.forEach(function (request, index) {
      if (showTypes[request.statusType]) {
        var icon = this.icons[request.statusType];
        var point = this.map.project(new L.LatLng(request.lat, request.long))._round()._subtract(tilePixelPoint);
        if (point.x > -15 && point.x < 270 && point.y > -41 && point.y < 296) {
          ctx.drawImage(icon.image, point.x + icon.offset.x, point.y + icon.offset.y);
          this._setFeatureAtPoint(tileKey, point, icon, request, index);
        }
      }
    }, this);
  },

  // ---------------------- FEATURE INTERACTION MANAGEMENT -------------------------

  _createEmptyFeatureTile: function (tilePoint) {
    this._featureMap[tilePoint] = {};
  },

  _setFeatureAtPoint: function (tilePoint, featurePoint, icon, feature, featureIndex) {
    // WARNING: This method is HOT. Measure performance thoroughly before making changes.

    // No need to do any checks here; _createEmptyFeatureTile() must always be run first
    var featureMap = this._featureMap[tilePoint];

    var resolution = this.interactionResolution || 1;

    var offsetX = featurePoint.x + icon.offset.x;
    var offsetY = featurePoint.y + icon.offset.y;
    // rectify offsets to interaction resolution
    offsetX = offsetX - (offsetX % resolution);
    offsetY = offsetY - (offsetY % resolution);
    var mask = icon.mask;
    // speediest looping
    var i = mask.length;
    while (i-- > 0) {
      var point = mask[i];
      var x = point.x + offsetX;
      var y = point.y + offsetY;
      // a tree structure for the map is very slow
      // making a key via (x + "," + y) is faster
      // a single number is fastest! (in V8)
      featureMap[y * 256 + x] = feature;
    }
  },

  _getFeatureAtPoint: function (tilePoint, featurePoint) {
    if (!featurePoint) {
      var point = tilePoint;
      tilePoint = new L.Point(Math.floor(point.x / 256), Math.floor(point.y / 256));
      featurePoint = new L.Point(point.x % 256, point.y % 256);
    }

    if (!this._featureMap) {
      this._featureMap = {};
    }

    var feature = null;
    var tile = this._featureMap[tilePoint];
    if (tile) {
      var resolution = this.interactionResolution || 1;
      var x = featurePoint.x - (featurePoint.x % resolution);
      var y = featurePoint.y - (featurePoint.y % resolution);
      feature = tile[y * 256 + x];
    }
    return feature;
  },

  // ---------------------- ICON MANAGEMENT -------------------------

  _initializeIcons: function () {
    this.icons = {
      open:   this._createIconImage(MapController.ICON_PATHS.default),
      opened: this._createIconImage(MapController.ICON_PATHS.opened),
      closed: this._createIconImage(MapController.ICON_PATHS.closed),
    }
  },

  _createIconImage: function (path) {
    var image = new Image();
    image.src = path;

    var info = {
      image: image,
      offset: {x: 0, y: 0},
      mask: []
    }
    image.representedObject = info;

    if (image.complete) {
      this._setupIcon(info, 2);
    }
    else {
      image.addEventListener("load", this, false);
      image.addEventListener("error", this, false);
    }

    return info;
  },

  _handleIconImageLoad: function (event) {
    event.target.removeEventListener("load", this, false);
    event.target.removeEventListener("error", this, false);
    // TODO: handle load failure
    if (event.type === "load") {
      this._setupIcon(event.target.representedObject);
    }

    if (this._iconsReady() && this._waitingToUpdate) {
      this.update();
    }
  },

  _setupIcon: function (icon) {
    icon.offset.x = -Math.floor(icon.image.width / 2);
    icon.offset.y = 1 - icon.image.height;

    // make the interaction mask
    var workCanvas = document.createElement("canvas");
    workCanvas.width = icon.image.width;
    workCanvas.height = icon.image.height;
    var workCtx = workCanvas.getContext("2d");
    workCtx.drawImage(icon.image, 0, 0);
    var iconData = workCtx.getImageData(0, 0, icon.image.width, icon.image.height).data;
    var rowLength = icon.image.width;

    var resolution = this.interactionResolution || 1;
    for (var i = 3, len = iconData.length; i < len; i += 4) {
      if (iconData[i] > 128) {
        var pixel = (i - 3) / 4;
        var y = Math.floor(pixel / rowLength);
        var x = pixel % rowLength;
        if (y % resolution === 0 && x % resolution === 0) {
          // note: an array of points is *much* faster than a tree structure to iterate through
          // could potentially save memory by saving a single number (256 * y + x)
          icon.mask.push({x: x, y: y});
        }
      }
    }
  },

  _iconsReady: function () {
    return this.icons.open.image.complete && this.icons.opened.image.complete && this.icons.closed.image.complete;
  },
};

<|endoftext|>


<commit_before>/**
	The MIT License (MIT)

	Copyright (c) 2014 MyChannel-Apps.de

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.

	@author		Adrian Preuß <Bizarrus>
*/

function KLink(text) {
	var _text			= '';
	var _command_left	= undefined;
	var _command_right	= undefined;
	var _hover			= true;

	function KLink(text) {
		_text = text || '';
	}

	function prepareLink(string) {
		return string.replace(/(<|>|\||°)/g, '\\$1');
	}

	this.setCommand = function(command_left, command_right) {
		_command_left	= command_left;
		_command_right	= command_right;
	};

	this.enableHover = function(state) {
		_hover = state;
	};

	this.toString = function() {
		var string = '°>';

		if(_text instanceof KImage) {
			string += _text.toString(true);

			// @ToDo The Hover
			string += '|' + _text.toString(true) + '<>--<>';
		} else {
			if(_hover == false) {
				string += '_h';
			}

			string += prepareLink(_text);
		}

		if(_command_left != undefined) {
			string += '|' + _command_left;
		}

		if(_command_right != undefined) {
			string += '|' + _command_right;
		}

		string += '<°';
		return string;
	};

	KLink(text);
};<commit_msg>KLink: binding hover image<commit_after>/**
	The MIT License (MIT)

	Copyright (c) 2014 MyChannel-Apps.de

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.

	@author		Adrian Preuß <Bizarrus>
*/

function KLink(text) {
	var _text			= '';
	var _command_left	= undefined;
	var _command_right	= undefined;
	var _hover			= true;
	var _hover_image	= undefined;

	function KLink(text) {
		_text = text || '';
	}

	function prepareLink(string) {
		return string.replace(/(<|>|\||°)/g, '\\$1');
	}

	this.setCommand = function(command_left, command_right) {
		_command_left	= command_left;
		_command_right	= command_right;
	};

	this.enableHover = function(state) {
		_hover = state;
	};

	this.setHoverImage = function(image) {
		if(_text instanceof KImage) {
			_hover_image = image;
		} else {
			Logger.warn('You can only use KImage.setHoverImage(image) when you bind a KImage!');
		}
	};

	this.toString = function() {
		var string = '°>';

		if(_text instanceof KImage) {
			string += _text.toString(true);

			// @ToDo The Hover
			string += '|' + (_hover_image == undefined ? _text.toString(true) : _hover_image.toString(true)) + '<>--<>';
		} else {
			if(_hover == false) {
				string += '_h';
			}

			string += prepareLink(_text);
		}

		if(_command_left != undefined) {
			string += '|' + _command_left;
		}

		if(_command_right != undefined) {
			string += '|' + _command_right;
		}

		string += '<°';
		return string;
	};

	KLink(text);
};<|endoftext|>


<commit_before>/*global define, require */
/*jshint indent:4 */

define(
    [
        "AppParams",
        "jquery",
        "underscore",
        "models/search",
        "text!templates/search/results.html",
        "text!templates/search/facets.html"
    ],
    function (AppParams, $, underscore, searchModel, resultsTemplate, facetsTemplate) {
        "use strict";

        var $body = $("body");
        var $searchPostsForm = $("#search-posts-form");
        var $searchText = $("#search-text");
        var $searchTypesNames = $('[name="type"]', $searchPostsForm);
        var $searchResults = $("#search-results");
        var $searchPriceMin = $("#search-price-min");
        var $searchPriceMax = $("#search-price-max");
        var $searchMake = $("#search-make");
        var $searchModel = $("#search-model");
        var $searchYear = $("#search-year");
        var $searchWhere = $("#search-where");
        var $searchUser = $("#search-user");
        var $searchTransmission = $("#search-transmission");
        var $searchTraction = $("#search-traction");
        var $searchHandicapped = $("#search-handicapped");
        var $searchArmor = $("#search-armor");
        var $facetsToggle = $("#facets-toggle");

        var currentPage;
        var currentSort;
        var pages;

        $(document.documentElement).on("keyup", function (e) {
            if (e.target.nodeName.toLowerCase() !== "body") {
                return;
            }

            if (e.keyCode === 191) {
                // focus on search by pressing slash (/)
                $searchText.focus();
            } else if (e.keyCode === 37 && typeof currentPage === "number" && currentPage > 1) {
                //pagination with left key
                search({page: currentPage - 1, sort: currentSort, scroll: true});
            } else if (e.keyCode === 39 && typeof currentPage === "number" && typeof pages === "number" && pages > currentPage) {
                //pagination with right key
                search({page: currentPage + 1, sort: currentSort, scroll: true});
            }
        });

        $searchResults.on("click", ".sortable a", function (e) {
            var target = e.currentTarget;
            e.preventDefault();

            var sortBy = target.getAttribute("data-sort");
            search({sort: sortBy});
        });

        $searchResults.on("click", ".pagination a", function (e) {
            var target = e.currentTarget;
            e.preventDefault();
            var page = target.getAttribute("data-page");
            var sort = target.getAttribute("data-sort");
            search({page: page, sort: sort, scroll: true});
        });

        var changeViewStyle = function (style) {
            if (style === "table") {
                $($(".results-thumbnail", $searchResults)[0]).addClass("hidden");
            } else {
                $($(".results-table", $searchResults)[0]).addClass("hidden");
            }

            $($(".results-" + style, $searchResults)[0]).removeClass("hidden");

            AppParams.postsViewStyle = style;

            $.ajax({
                url: AppParams.webroot + "/search?posts_view_style=" + style,
                type: "HEAD"
            });
        };

        $searchResults.on("click", ".posts-view-style-table", function (e) {
            changeViewStyle("table");
            e.preventDefault();
        });

        $searchResults.on("click", ".posts-view-style-thumbnail", function (e) {
            changeViewStyle("thumbnail");
            e.preventDefault();
        });

        var removeFilters = function () {
            $searchPostsForm.find(':input').each(function (e) {
                var type = this.type;
                var tag = this.tagName.toLowerCase();

                if (this.name !== "q") {
                    if (type === 'text' || type === 'password' || tag === 'textarea') {
                        this.value = '';
                    } else if (type === 'checkbox' || type === 'radio') {
                        this.checked = false;
                    } else if (tag === 'select') {
                        this.selectedIndex = -1;
                    }
                }
            });

            $('[name="type"][value=""]', $searchPostsForm).prop("checked", true);
            $('[name="persist-username"][value=""]', $searchPostsForm).prop("checked", true);
        };

        var search = function (data) {
            var persistUsernameValue = $("[name=persist-username]:checked").val();

            if (persistUsernameValue) {
                $searchUser.val(persistUsernameValue);
            }

            if (data === undefined) {
                data = {};
            }

            var sort = data.sort || "";
            var page = data.page || 1;
            var scroll = data.scroll || false;

            if (scroll) {
                $body.animate({
                    scrollTop: ($searchResults.offset().top) - 50
                }, 500);
            }

            var formSerialized = $(':input', $searchPostsForm).filter(
                function () {
                    return $(this).val();
                }
            ).serialize();

            var size = 20;
            var from = 1;
            var to;
            var total;

            if (page !== undefined) {

                page = parseInt(page, 10);

                if (isNaN(page)) {
                    page = 1;
                }

                from = ((page - 1) * size) + 1;
            } else {
                page = 1;
            }

            $.ajax({
                data: formSerialized,
                url: AppParams.webroot + "/search-engine?size=" + encodeURIComponent(size) + "&from=" + encodeURIComponent(from) + "&sort=" + encodeURIComponent(sort),
                type: "GET",
                success: function (result) {
                    total = result.hits.total;
                    to = from + size - 1;

                    pages = Math.ceil(total / size);

                    if (to > total) {
                        to = total;
                    }

                    var compiledResults = underscore.template(resultsTemplate);
                    var compiledFacets = underscore.template(facetsTemplate);

                    var viewStyle;

                    if (AppParams.postsViewStyle === "table") {
                        viewStyle = "table";
                    } else {
                        viewStyle = "thumbnail";
                    }

                    currentPage = page;
                    currentSort = sort;

                    var searchParams = searchModel.parseQueryString(formSerialized);

                    if (currentPage !== 1) {
                        searchParams.page = currentPage;
                    }

                    if (sort) {
                        searchParams.sort = sort;
                    }

                    if (searchParams["price-min"]) {
                        searchParams["price-min"] = decodeURIComponent(searchParams["price-min"]).replace(/[^0-9]/g, '');
                    }

                    if (searchParams["price-max"]) {
                        searchParams["price-max"] = decodeURIComponent(searchParams["price-max"]).replace(/[^0-9]/g, '');
                    }

                    var oldSearchParams = searchModel.parseLocationQueryString(window);

                    var searchParamsEqual = underscore.isEqual(searchParams, oldSearchParams);

                    if ((window.history && window.history.pushState && ! searchParamsEqual) ||
                            window.location.pathname !== AppParams.webroot + "/search") {
                        var address = AppParams.webroot + "/search";

                        var part = $.param(searchParams);

                        if (part === "=") {
                            part = "q=";
                        }

                        address += "?" + decodeURIComponent(part).replace(/%2B/g, '+');

                        window.history.pushState(null, null, address);
                    }

                    var searchParamsTotal = underscore.size(searchParams);

                    var facetsHtml = compiledFacets(
                        {
                            pageLink : searchModel.pageLink,
                            formatMoney : searchModel.formatMoney,
                            termListHtmlElements : searchModel.termListHtmlElements,
                            termListHtmlElementsType : searchModel.termListHtmlElementsType,
                            termListHtmlElementsBool : searchModel.termListHtmlElementsBool,
                            formSerialized : formSerialized,
                            transmissionTranslation : searchModel.transmissionTranslation,
                            tractionTranslation : searchModel.tractionTranslation,
                            facets : result.facets,
                            searchParamsTotal : searchParamsTotal,
                            currentQueryStringParams : searchModel.parseLocationQueryString()
                        }
                    );

                    var getFirstPictureAddress = function (pictures, cropOptions) {
                        var picture;
                        if (pictures !== undefined && pictures !== null && pictures[0] !== undefined) {
                            picture = pictures[0];
                        } else {
                            picture = {
                                "picture_id" : AppParams.placeholder
                            };
                        }

                        var cropSubPath = "";
                        if (picture.crop_options) {
                            cropSubPath = picture.crop_options.x + "x" + picture.crop_options.y + ":" +
                                picture.crop_options.x2 + "x" + picture.crop_options.y2 + "/"
                            ;
                        }

                        cropSubPath = cropSubPath + cropOptions;

                        return AppParams.imagesCdn + "/unsafe/" + cropSubPath + picture.picture_id + ".jpg";
                    };

                    if ($facetsToggle.hasClass("hidden")) {
                        $facetsToggle.removeClass("hidden");
                    }

                    $searchResults.html(compiledResults(
                        {
                            facetsHtml : facetsHtml,
                            pageLink : searchModel.pageLink,
                            formatMoney : searchModel.formatMoney,
                            AppParams : AppParams,
                            getFirstPictureAddress : getFirstPictureAddress,
                            result : result,
                            size : size,
                            from : from,
                            to : to,
                            total : total,
                            sort : sort,
                            viewStyle : viewStyle,
                            formSerialized : formSerialized,
                            currentPage : currentPage,
                            pages : pages
                        }
                    ));

                    var $priceInputs = $(".price-inputs", $searchResults);
                    var $priceMinInput = $(".price-min-input", $searchResults);
                    var $priceMaxInput = $(".price-max-input", $searchResults);

                    $priceMinInput.val(searchModel.formatMoney($searchPriceMin.val()));
                    $priceMaxInput.val(searchModel.formatMoney($searchPriceMax.val()));

                    $priceInputs.tooltip();

                    searchModel.maskMoney($priceMinInput);
                    searchModel.maskMoney($priceMaxInput);

                    $priceInputs.on("keyup", function (e) {
                        if (e.keyCode === 13) {
                            var priceMin = $priceMinInput.val().replace(/[^\d]/g, "");
                            var priceMax = $priceMaxInput.val().replace(/[^\d]/g, "");
                            $searchPriceMin.val(priceMin);
                            $searchPriceMax.val(priceMax);
                            search();
                        }
                    });
                }
            });
        };

        var changeSearchTermByUrl = function (urlParams, $termObject, name) {
            if (urlParams[name] !== undefined) {
                $termObject.val(decodeURIComponent(urlParams[name].replace(/\+/gi, " ")));
            } else {
                $termObject.val("");
            }
        };

        var changeSearchTermsByUrlParams = function (urlParams) {
            changeSearchTermByUrl(urlParams, $searchPriceMin, "price-min");
            changeSearchTermByUrl(urlParams, $searchPriceMax, "price-max");

            if (urlParams.type !== undefined) {
                $searchTypesNames.filter('[value="' + underscore.escape(urlParams.type) + '"]').prop("checked", true);
            }

            changeSearchTermByUrl(urlParams, $searchMake, "make");
            changeSearchTermByUrl(urlParams, $searchModel, "model");
            changeSearchTermByUrl(urlParams, $searchYear, "year");
            changeSearchTermByUrl(urlParams, $searchWhere, "where");
            changeSearchTermByUrl(urlParams, $searchUser, "u");
            changeSearchTermByUrl(urlParams, $searchTransmission, "transmission");
            changeSearchTermByUrl(urlParams, $searchTraction, "traction");
            changeSearchTermByUrl(urlParams, $searchHandicapped, "handicapped");
            changeSearchTermByUrl(urlParams, $searchArmor, "armor");
        };

        var urlParams = searchModel.parseLocationQueryString();

        changeSearchTermsByUrlParams(urlParams);

        // if the query is defined on the page load, search by it
        // otherwise, if the search is accessed without a query and not on the index page,
        // focus it, and if the page is in the index page, load the index js file
        if (urlParams.q !== undefined) {
            $searchText.val(decodeURIComponent(urlParams.q.replace(/\+/gi, " ")));
            search({page: urlParams.page || 1, sort: urlParams.sort});
        } else if (window.location.pathname === "/") {
            require(["views/index/index"], function () {
            });
        } else {
            $searchText.focus();
        }

        window.onpopstate = function (event) {
            var urlParams = searchModel.parseLocationQueryString();

            changeSearchTermsByUrlParams(urlParams);

            if (urlParams.q !== undefined) {
                $searchText.val(decodeURIComponent(urlParams.q.replace(/\+/gi, " ")));
                search({page: urlParams.page || 1, sort: urlParams.sort});
            }
        };

        $searchText.on("change", function (e) {
            setTimeout(function () {
                search();
            }, 200);

        });
        $searchText.on("paste", function (e) {
            setTimeout(function () {
                search();
            }, 200);
        });

        $searchText.on("keyup", function (e) {
            var q = $searchText.val();

            //only go ahead if the keyCode is a "printable character" or erase / delete, return
            var key = e.keyCode;
            if ((key >= 48 && key <= 90) || (key >= 188 && key <= 222) || key === 8 || key === 46 || key === 13) {
                search();
            }
        });

        $searchResults.on("click", ".remove-filters", function (e) {
            removeFilters();
            search();
        });

        $searchResults.on("click", ".search-facets a", function (e) {
            var target = e.currentTarget;

            e.preventDefault();

            var name = target.getAttribute("data-name");
            var value = target.getAttribute("data-value");

            var $input = $('[name="' + underscore.escape(name) + '"]', $searchPostsForm);

            if (name === "price") {
                $searchPriceMin.val(target.getAttribute("data-price-from"));
                $searchPriceMax.val(target.getAttribute("data-price-to"));

            } else if ($input.attr("type") === "radio") {
                $input.filter('[value="' + underscore.escape(value) + '"]').prop("checked", true);
            } else {
                $input.val(value);
            }

            search();
        });

        $facetsToggle.on("click", function (e) {
            e.preventDefault();
            var $searchFacets = $(".search-facets", $searchResults);
            if ($facetsToggle.hasClass("active")) {
                $searchFacets.addClass("hidden-phone");
            } else {
                $searchFacets.removeClass("hidden-phone");
            }
        });
    }
);
<commit_msg>verify if the user is on the / (home) page instead of checking if the q value is entered first, else verify it<commit_after>/*global define, require */
/*jshint indent:4 */

define(
    [
        "AppParams",
        "jquery",
        "underscore",
        "models/search",
        "text!templates/search/results.html",
        "text!templates/search/facets.html"
    ],
    function (AppParams, $, underscore, searchModel, resultsTemplate, facetsTemplate) {
        "use strict";

        var $body = $("body");
        var $searchPostsForm = $("#search-posts-form");
        var $searchText = $("#search-text");
        var $searchTypesNames = $('[name="type"]', $searchPostsForm);
        var $searchResults = $("#search-results");
        var $searchPriceMin = $("#search-price-min");
        var $searchPriceMax = $("#search-price-max");
        var $searchMake = $("#search-make");
        var $searchModel = $("#search-model");
        var $searchYear = $("#search-year");
        var $searchWhere = $("#search-where");
        var $searchUser = $("#search-user");
        var $searchTransmission = $("#search-transmission");
        var $searchTraction = $("#search-traction");
        var $searchHandicapped = $("#search-handicapped");
        var $searchArmor = $("#search-armor");
        var $facetsToggle = $("#facets-toggle");

        var currentPage;
        var currentSort;
        var pages;

        $(document.documentElement).on("keyup", function (e) {
            if (e.target.nodeName.toLowerCase() !== "body") {
                return;
            }

            if (e.keyCode === 191) {
                // focus on search by pressing slash (/)
                $searchText.focus();
            } else if (e.keyCode === 37 && typeof currentPage === "number" && currentPage > 1) {
                //pagination with left key
                search({page: currentPage - 1, sort: currentSort, scroll: true});
            } else if (e.keyCode === 39 && typeof currentPage === "number" && typeof pages === "number" && pages > currentPage) {
                //pagination with right key
                search({page: currentPage + 1, sort: currentSort, scroll: true});
            }
        });

        $searchResults.on("click", ".sortable a", function (e) {
            var target = e.currentTarget;
            e.preventDefault();

            var sortBy = target.getAttribute("data-sort");
            search({sort: sortBy});
        });

        $searchResults.on("click", ".pagination a", function (e) {
            var target = e.currentTarget;
            e.preventDefault();
            var page = target.getAttribute("data-page");
            var sort = target.getAttribute("data-sort");
            search({page: page, sort: sort, scroll: true});
        });

        var changeViewStyle = function (style) {
            if (style === "table") {
                $($(".results-thumbnail", $searchResults)[0]).addClass("hidden");
            } else {
                $($(".results-table", $searchResults)[0]).addClass("hidden");
            }

            $($(".results-" + style, $searchResults)[0]).removeClass("hidden");

            AppParams.postsViewStyle = style;

            $.ajax({
                url: AppParams.webroot + "/search?posts_view_style=" + style,
                type: "HEAD"
            });
        };

        $searchResults.on("click", ".posts-view-style-table", function (e) {
            changeViewStyle("table");
            e.preventDefault();
        });

        $searchResults.on("click", ".posts-view-style-thumbnail", function (e) {
            changeViewStyle("thumbnail");
            e.preventDefault();
        });

        var removeFilters = function () {
            $searchPostsForm.find(':input').each(function (e) {
                var type = this.type;
                var tag = this.tagName.toLowerCase();

                if (this.name !== "q") {
                    if (type === 'text' || type === 'password' || tag === 'textarea') {
                        this.value = '';
                    } else if (type === 'checkbox' || type === 'radio') {
                        this.checked = false;
                    } else if (tag === 'select') {
                        this.selectedIndex = -1;
                    }
                }
            });

            $('[name="type"][value=""]', $searchPostsForm).prop("checked", true);
            $('[name="persist-username"][value=""]', $searchPostsForm).prop("checked", true);
        };

        var search = function (data) {
            var persistUsernameValue = $("[name=persist-username]:checked").val();

            if (persistUsernameValue) {
                $searchUser.val(persistUsernameValue);
            }

            if (data === undefined) {
                data = {};
            }

            var sort = data.sort || "";
            var page = data.page || 1;
            var scroll = data.scroll || false;

            if (scroll) {
                $body.animate({
                    scrollTop: ($searchResults.offset().top) - 50
                }, 500);
            }

            var formSerialized = $(':input', $searchPostsForm).filter(
                function () {
                    return $(this).val();
                }
            ).serialize();

            var size = 20;
            var from = 1;
            var to;
            var total;

            if (page !== undefined) {

                page = parseInt(page, 10);

                if (isNaN(page)) {
                    page = 1;
                }

                from = ((page - 1) * size) + 1;
            } else {
                page = 1;
            }

            $.ajax({
                data: formSerialized,
                url: AppParams.webroot + "/search-engine?size=" + encodeURIComponent(size) + "&from=" + encodeURIComponent(from) + "&sort=" + encodeURIComponent(sort),
                type: "GET",
                success: function (result) {
                    total = result.hits.total;
                    to = from + size - 1;

                    pages = Math.ceil(total / size);

                    if (to > total) {
                        to = total;
                    }

                    var compiledResults = underscore.template(resultsTemplate);
                    var compiledFacets = underscore.template(facetsTemplate);

                    var viewStyle;

                    if (AppParams.postsViewStyle === "table") {
                        viewStyle = "table";
                    } else {
                        viewStyle = "thumbnail";
                    }

                    currentPage = page;
                    currentSort = sort;

                    var searchParams = searchModel.parseQueryString(formSerialized);

                    if (currentPage !== 1) {
                        searchParams.page = currentPage;
                    }

                    if (sort) {
                        searchParams.sort = sort;
                    }

                    if (searchParams["price-min"]) {
                        searchParams["price-min"] = decodeURIComponent(searchParams["price-min"]).replace(/[^0-9]/g, '');
                    }

                    if (searchParams["price-max"]) {
                        searchParams["price-max"] = decodeURIComponent(searchParams["price-max"]).replace(/[^0-9]/g, '');
                    }

                    var oldSearchParams = searchModel.parseLocationQueryString(window);

                    var searchParamsEqual = underscore.isEqual(searchParams, oldSearchParams);

                    if ((window.history && window.history.pushState && ! searchParamsEqual) ||
                            window.location.pathname !== AppParams.webroot + "/search") {
                        var address = AppParams.webroot + "/search";

                        var part = $.param(searchParams);

                        if (part === "=") {
                            part = "q=";
                        }

                        address += "?" + decodeURIComponent(part).replace(/%2B/g, '+');

                        window.history.pushState(null, null, address);
                    }

                    var searchParamsTotal = underscore.size(searchParams);

                    var facetsHtml = compiledFacets(
                        {
                            pageLink : searchModel.pageLink,
                            formatMoney : searchModel.formatMoney,
                            termListHtmlElements : searchModel.termListHtmlElements,
                            termListHtmlElementsType : searchModel.termListHtmlElementsType,
                            termListHtmlElementsBool : searchModel.termListHtmlElementsBool,
                            formSerialized : formSerialized,
                            transmissionTranslation : searchModel.transmissionTranslation,
                            tractionTranslation : searchModel.tractionTranslation,
                            facets : result.facets,
                            searchParamsTotal : searchParamsTotal,
                            currentQueryStringParams : searchModel.parseLocationQueryString()
                        }
                    );

                    var getFirstPictureAddress = function (pictures, cropOptions) {
                        var picture;
                        if (pictures !== undefined && pictures !== null && pictures[0] !== undefined) {
                            picture = pictures[0];
                        } else {
                            picture = {
                                "picture_id" : AppParams.placeholder
                            };
                        }

                        var cropSubPath = "";
                        if (picture.crop_options) {
                            cropSubPath = picture.crop_options.x + "x" + picture.crop_options.y + ":" +
                                picture.crop_options.x2 + "x" + picture.crop_options.y2 + "/"
                            ;
                        }

                        cropSubPath = cropSubPath + cropOptions;

                        return AppParams.imagesCdn + "/unsafe/" + cropSubPath + picture.picture_id + ".jpg";
                    };

                    if ($facetsToggle.hasClass("hidden")) {
                        $facetsToggle.removeClass("hidden");
                    }

                    $searchResults.html(compiledResults(
                        {
                            facetsHtml : facetsHtml,
                            pageLink : searchModel.pageLink,
                            formatMoney : searchModel.formatMoney,
                            AppParams : AppParams,
                            getFirstPictureAddress : getFirstPictureAddress,
                            result : result,
                            size : size,
                            from : from,
                            to : to,
                            total : total,
                            sort : sort,
                            viewStyle : viewStyle,
                            formSerialized : formSerialized,
                            currentPage : currentPage,
                            pages : pages
                        }
                    ));

                    var $priceInputs = $(".price-inputs", $searchResults);
                    var $priceMinInput = $(".price-min-input", $searchResults);
                    var $priceMaxInput = $(".price-max-input", $searchResults);

                    $priceMinInput.val(searchModel.formatMoney($searchPriceMin.val()));
                    $priceMaxInput.val(searchModel.formatMoney($searchPriceMax.val()));

                    $priceInputs.tooltip();

                    searchModel.maskMoney($priceMinInput);
                    searchModel.maskMoney($priceMaxInput);

                    $priceInputs.on("keyup", function (e) {
                        if (e.keyCode === 13) {
                            var priceMin = $priceMinInput.val().replace(/[^\d]/g, "");
                            var priceMax = $priceMaxInput.val().replace(/[^\d]/g, "");
                            $searchPriceMin.val(priceMin);
                            $searchPriceMax.val(priceMax);
                            search();
                        }
                    });
                }
            });
        };

        var changeSearchTermByUrl = function (urlParams, $termObject, name) {
            if (urlParams[name] !== undefined) {
                $termObject.val(decodeURIComponent(urlParams[name].replace(/\+/gi, " ")));
            } else {
                $termObject.val("");
            }
        };

        var changeSearchTermsByUrlParams = function (urlParams) {
            changeSearchTermByUrl(urlParams, $searchPriceMin, "price-min");
            changeSearchTermByUrl(urlParams, $searchPriceMax, "price-max");

            if (urlParams.type !== undefined) {
                $searchTypesNames.filter('[value="' + underscore.escape(urlParams.type) + '"]').prop("checked", true);
            }

            changeSearchTermByUrl(urlParams, $searchMake, "make");
            changeSearchTermByUrl(urlParams, $searchModel, "model");
            changeSearchTermByUrl(urlParams, $searchYear, "year");
            changeSearchTermByUrl(urlParams, $searchWhere, "where");
            changeSearchTermByUrl(urlParams, $searchUser, "u");
            changeSearchTermByUrl(urlParams, $searchTransmission, "transmission");
            changeSearchTermByUrl(urlParams, $searchTraction, "traction");
            changeSearchTermByUrl(urlParams, $searchHandicapped, "handicapped");
            changeSearchTermByUrl(urlParams, $searchArmor, "armor");
        };

        var urlParams = searchModel.parseLocationQueryString();

        changeSearchTermsByUrlParams(urlParams);

        // if the query is defined on the page load, search by it
        // otherwise, if the search is accessed without a query and not on the index page,
        // focus it, and if the page is in the index page, load the index js file
        if (window.location.pathname === "/") {
            require(["views/index/index"], function () {
            });
        } else if (urlParams.q !== undefined) {
            $searchText.val(decodeURIComponent(urlParams.q.replace(/\+/gi, " ")));
            search({page: urlParams.page || 1, sort: urlParams.sort});
        } else {
            $searchText.focus();
        }

        window.onpopstate = function (event) {
            var urlParams = searchModel.parseLocationQueryString();

            changeSearchTermsByUrlParams(urlParams);

            if (urlParams.q !== undefined) {
                $searchText.val(decodeURIComponent(urlParams.q.replace(/\+/gi, " ")));
                search({page: urlParams.page || 1, sort: urlParams.sort});
            }
        };

        $searchText.on("change", function (e) {
            setTimeout(function () {
                search();
            }, 200);

        });
        $searchText.on("paste", function (e) {
            setTimeout(function () {
                search();
            }, 200);
        });

        $searchText.on("keyup", function (e) {
            var q = $searchText.val();

            //only go ahead if the keyCode is a "printable character" or erase / delete, return
            var key = e.keyCode;
            if ((key >= 48 && key <= 90) || (key >= 188 && key <= 222) || key === 8 || key === 46 || key === 13) {
                search();
            }
        });

        $searchResults.on("click", ".remove-filters", function (e) {
            removeFilters();
            search();
        });

        $searchResults.on("click", ".search-facets a", function (e) {
            var target = e.currentTarget;

            e.preventDefault();

            var name = target.getAttribute("data-name");
            var value = target.getAttribute("data-value");

            var $input = $('[name="' + underscore.escape(name) + '"]', $searchPostsForm);

            if (name === "price") {
                $searchPriceMin.val(target.getAttribute("data-price-from"));
                $searchPriceMax.val(target.getAttribute("data-price-to"));

            } else if ($input.attr("type") === "radio") {
                $input.filter('[value="' + underscore.escape(value) + '"]').prop("checked", true);
            } else {
                $input.val(value);
            }

            search();
        });

        $facetsToggle.on("click", function (e) {
            e.preventDefault();
            var $searchFacets = $(".search-facets", $searchResults);
            if ($facetsToggle.hasClass("active")) {
                $searchFacets.addClass("hidden-phone");
            } else {
                $searchFacets.removeClass("hidden-phone");
            }
        });
    }
);
<|endoftext|>


<commit_before>import _ from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
import { modules as uiModules } from 'stripes-config'; // eslint-disable-line

/* eslint-disable import/extensions */
import stripesCore from '@folio/stripes-core/package.json'; // eslint-disable-line
import stripesConnect from '@folio/stripes-connect/package.json';
import stripesComponents from '@folio/stripes-components/package.json';
import stripesLogger from '@folio/stripes-logger/package.json';
/* eslint-enable */

import Pane from '@folio/stripes-components/lib/Pane';
import Headline from '@folio/stripes-components/lib/Headline';
import List from '@folio/stripes-components/lib/List';
import Paneset from '@folio/stripes-components/lib/Paneset';
import { isVersionCompatible } from '../discoverServices';
import AboutEnabledModules from './AboutEnabledModules';

const About = (props) => {
  function renderDependencies(m, interfaces) {
    const base = `${m.module} ${m.version}`;
    if (!interfaces) {
      return base;
    }

    const okapiInterfaces = m.okapiInterfaces;
    if (!okapiInterfaces) {
      return `${base} declares no dependencies`;
    }

    const itemFormatter = (key) => {
      const required = okapiInterfaces[key];
      const available = interfaces[key];
      let style = {};
      let text = required;

      if (!available) {
        style = { color: 'red', fontWeight: 'bold' };
      } else if (!isVersionCompatible(available, required)) {
        style = { color: 'orange' };
        text = `${required} (${available} available)`;
      }

      return
{key} {text}
;
    };

    return (
      <Headline size="small" faded>{m.module} {m.version} depends on:</Headline>
      <List
        items={Object.keys(okapiInterfaces)}
        itemFormatter={itemFormatter}
        listStyle="bullets"
      />
    );
  }

  function listModules(caption, list, interfaces) {
    const n = list.length;
    const itemFormatter = m => (
{renderDependencies(m, interfaces)}
);
    return (

        <Headline>{n} {caption} module{n === 1 ? '' : 's'}</Headline>
        <List
          listStyle="bullets"
          items={list}
          itemFormatter={itemFormatter}
        />

    );
  }

  const modules = _.get(props.stripes, ['discovery', 'modules']) || {};
  const interfaces = _.get(props.stripes, ['discovery', 'interfaces']) || {};
  const nm = Object.keys(modules).length;
  const ni = Object.keys(interfaces).length;
  const ConnectedAboutEnabledModules = props.stripes.connect(AboutEnabledModules);

  return (
    <Paneset nested defaultWidth="80%">
      <Pane defaultWidth="30%" paneTitle="User interface">
        <Headline>Foundation</Headline>

        <List
          listStyle="bullets"
          items={[
            {
              key: 'stripes-core',
              value: `stripes-core ${stripesCore.version}`,
            },
            {
              key: 'stripes-connect',
              value: `stripes-connect ${stripesConnect.version}`,
            },
            {
              key: 'stripes-components',
              value: `stripes-components ${stripesComponents.version}`,
            },
            {
              key: 'stripes-logger',
              value: `stripes-logger ${stripesLogger.version}`,
            },
          ]}
          itemFormatter={item => (
{item.value}
)}
        />

        {Object.keys(uiModules).map(key => listModules(key, uiModules[key]))}
      </Pane>
      <Pane defaultWidth="30%" paneTitle="Okapi services">
        <Headline>Okapi</Headline>
        <List
          listStyle="bullets"
          itemFormatter={(item, i) => (
{item}
)}
          items={[
            `Version ${_.get(props.stripes, ['discovery', 'okapi']) || 'unknown'}`,
            `For tenant ${_.get(props.stripes, ['okapi', 'tenant']) || 'unknown'}`,
            `On URL ${_.get(props.stripes, ['okapi', 'url']) || 'unknown'}`,
          ]}
        />

        <Headline>{nm} module{nm === 1 ? '' : 's'}</Headline>
        <ConnectedAboutEnabledModules tenantid={_.get(props.stripes, ['okapi', 'tenant']) || 'unknown'} availableModules={modules} />

          Key.

          Installed modules that are not enabled for this tenant are
          displayed in gray.

        <Headline>{ni} interface{ni === 1 ? '' : 's'}</Headline>
        <List
          listStyle="bullets"
          items={Object.keys(interfaces).sort()}
          itemFormatter={key => (
{key} {interfaces[key]}
)}
        />
      </Pane>
      <Pane defaultWidth="40%" paneTitle="UI/service dependencies">
        <Headline>Foundation</Headline>
        {renderDependencies(Object.assign({}, stripesCore.stripes || {}, { module: 'stripes-core' }), interfaces)}

        {Object.keys(uiModules).map(key => listModules(key, uiModules[key], interfaces))}

          Key.

          Interfaces that are required but absent are highlighted
          in bold red.

          Interfaces that are required but present only in an incompatible version are highlighted
          in orange.

          Interfaces that are present in a compatible version are shown in regular font.

      </Pane>
    </Paneset>
  );
};

About.propTypes = {
  stripes: PropTypes.shape({
    discovery: PropTypes.shape({
      modules: PropTypes.object,
      interfaces: PropTypes.object,
    }),
    connect: PropTypes.func,
  }).isRequired,
};

export default About;
<commit_msg>Use relative import for files within current package<commit_after>import _ from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
import { modules as uiModules } from 'stripes-config'; // eslint-disable-line

/* eslint-disable import/extensions */
import stripesConnect from '@folio/stripes-connect/package.json';
import stripesComponents from '@folio/stripes-components/package.json';
import stripesLogger from '@folio/stripes-logger/package.json';
/* eslint-enable */

import Pane from '@folio/stripes-components/lib/Pane';
import Headline from '@folio/stripes-components/lib/Headline';
import List from '@folio/stripes-components/lib/List';
import Paneset from '@folio/stripes-components/lib/Paneset';
import { isVersionCompatible } from '../discoverServices';
import AboutEnabledModules from './AboutEnabledModules';

import stripesCore from '../../package.json'; // eslint-disable-line

const About = (props) => {
  function renderDependencies(m, interfaces) {
    const base = `${m.module} ${m.version}`;
    if (!interfaces) {
      return base;
    }

    const okapiInterfaces = m.okapiInterfaces;
    if (!okapiInterfaces) {
      return `${base} declares no dependencies`;
    }

    const itemFormatter = (key) => {
      const required = okapiInterfaces[key];
      const available = interfaces[key];
      let style = {};
      let text = required;

      if (!available) {
        style = { color: 'red', fontWeight: 'bold' };
      } else if (!isVersionCompatible(available, required)) {
        style = { color: 'orange' };
        text = `${required} (${available} available)`;
      }

      return
{key} {text}
;
    };

    return (
      <Headline size="small" faded>{m.module} {m.version} depends on:</Headline>
      <List
        items={Object.keys(okapiInterfaces)}
        itemFormatter={itemFormatter}
        listStyle="bullets"
      />
    );
  }

  function listModules(caption, list, interfaces) {
    const n = list.length;
    const itemFormatter = m => (
{renderDependencies(m, interfaces)}
);
    return (

        <Headline>{n} {caption} module{n === 1 ? '' : 's'}</Headline>
        <List
          listStyle="bullets"
          items={list}
          itemFormatter={itemFormatter}
        />

    );
  }

  const modules = _.get(props.stripes, ['discovery', 'modules']) || {};
  const interfaces = _.get(props.stripes, ['discovery', 'interfaces']) || {};
  const nm = Object.keys(modules).length;
  const ni = Object.keys(interfaces).length;
  const ConnectedAboutEnabledModules = props.stripes.connect(AboutEnabledModules);

  return (
    <Paneset nested defaultWidth="80%">
      <Pane defaultWidth="30%" paneTitle="User interface">
        <Headline>Foundation</Headline>

        <List
          listStyle="bullets"
          items={[
            {
              key: 'stripes-core',
              value: `stripes-core ${stripesCore.version}`,
            },
            {
              key: 'stripes-connect',
              value: `stripes-connect ${stripesConnect.version}`,
            },
            {
              key: 'stripes-components',
              value: `stripes-components ${stripesComponents.version}`,
            },
            {
              key: 'stripes-logger',
              value: `stripes-logger ${stripesLogger.version}`,
            },
          ]}
          itemFormatter={item => (
{item.value}
)}
        />

        {Object.keys(uiModules).map(key => listModules(key, uiModules[key]))}
      </Pane>
      <Pane defaultWidth="30%" paneTitle="Okapi services">
        <Headline>Okapi</Headline>
        <List
          listStyle="bullets"
          itemFormatter={(item, i) => (
{item}
)}
          items={[
            `Version ${_.get(props.stripes, ['discovery', 'okapi']) || 'unknown'}`,
            `For tenant ${_.get(props.stripes, ['okapi', 'tenant']) || 'unknown'}`,
            `On URL ${_.get(props.stripes, ['okapi', 'url']) || 'unknown'}`,
          ]}
        />

        <Headline>{nm} module{nm === 1 ? '' : 's'}</Headline>
        <ConnectedAboutEnabledModules tenantid={_.get(props.stripes, ['okapi', 'tenant']) || 'unknown'} availableModules={modules} />

          Key.

          Installed modules that are not enabled for this tenant are
          displayed in gray.

        <Headline>{ni} interface{ni === 1 ? '' : 's'}</Headline>
        <List
          listStyle="bullets"
          items={Object.keys(interfaces).sort()}
          itemFormatter={key => (
{key} {interfaces[key]}
)}
        />
      </Pane>
      <Pane defaultWidth="40%" paneTitle="UI/service dependencies">
        <Headline>Foundation</Headline>
        {renderDependencies(Object.assign({}, stripesCore.stripes || {}, { module: 'stripes-core' }), interfaces)}

        {Object.keys(uiModules).map(key => listModules(key, uiModules[key], interfaces))}

          Key.

          Interfaces that are required but absent are highlighted
          in bold red.

          Interfaces that are required but present only in an incompatible version are highlighted
          in orange.

          Interfaces that are present in a compatible version are shown in regular font.

      </Pane>
    </Paneset>
  );
};

About.propTypes = {
  stripes: PropTypes.shape({
    discovery: PropTypes.shape({
      modules: PropTypes.object,
      interfaces: PropTypes.object,
    }),
    connect: PropTypes.func,
  }).isRequired,
};

export default About;
<|endoftext|>


<commit_before>(function() {
  'use strict';

  domready(function() {

    var scrollElements = document.querySelectorAll('[data-scroll-element]');

    emailFormHandler();
    facebookHandler();
    twitterHandler();
    emailHandler();

    var inValidRange = function(offset, limit) {
      return offset >= 0 && offset < limit;
    };

    var setVisibility = function(element, nextOffset, limit) {
      if(inValidRange(nextOffset, limit)) {
        my_removeClass(element, 'disabled');
      } else {
        my_addClass(element, 'disabled');
      }
    };

    var setVisibilityAll = function(elements, newOffset) {
      var
        scrollElement,
        increment,
        nextOffset,
        limit;

      each(elements, function(el) {
        scrollElement = document.querySelector(el.dataset.scrollElement);
        increment  = parseInt(el.dataset.scrollIncrement);
        nextOffset = newOffset + increment;
        limit      = parseInt(scrollElement.dataset.scrollLimit);

        setVisibility(el, nextOffset, limit);
      });
    };

    var resetScroll = function(element) {
      element.scrollTop = 0;
      element.dataset.scrollOffset = 0;
    };

    each(document.querySelectorAll('[data-clipboard-target]'), function(el) {
      var clipboard = new Clipboard(el);
      var notification;

      var notify = function(clipboardNotification, notificationText) {
        notification = document.getElementById(clipboardNotification.slice(1));
        notification.textContent = notificationText;
        my_addClass(notification, 'in');
        setTimeout(function() {
          my_removeClass(notification, 'in');
        }, 1400);
      };

      var notifySuccess = function(e) {
        notify(e.trigger.dataset.clipboardNotification, "Copied!");
      };

      var notifyFailure = function(e) {
        //if the copy function failed the text should still be selected, so just ask the user to hit ctrl+c
        notify(e.trigger.dataset.clipboardNotification, "Press Ctrl+C to copy");
      };

      clipboard.on('success', notifySuccess);
      clipboard.on('error', notifyFailure);
      handleClicks(el, function(e) {
        if (window.frameElement && window.frameElement.squatchJsApi) {
          window.frameElement.squatchJsApi._shareEvent(window.squatch, 'DIRECT');
        }
      });
    });

    each(scrollElements, function(el) {
      var element = document.querySelector(el.dataset.scrollElement);
      var increment = parseInt(el.dataset.scrollIncrement);
      var limit     = parseInt(element.dataset.scrollLimit.valueOf());
      var offset    = parseInt(element.dataset.scrollOffset.valueOf());
      var newOffset;

      element.dataset.scrollLimit = limit;

      var nextOffset = offset + increment;

      setVisibility(el, nextOffset, limit);

      // Force IE to forget previous scroll top value
      resetScroll(element);

      listenToClick(el, 'click', function() {
        offset = parseInt(element.dataset.scrollOffset);

        newOffset = offset + increment;

        if (inValidRange(newOffset, limit)) {
          scrollTop(element, document.getElementById(newOffset).offsetTop, 400);
          element.dataset.scrollOffset = newOffset;

          setVisibilityAll(scrollElements, newOffset);
        }
      });
    });

    each(document.querySelectorAll('[data-moment]'), function(el) {
      var time = moment(parseInt(el.textContent));
      el.textContent = time.fromNow();
    });

    each(document.getElementsByClassName('squatch-header-close'), function(el) {
      handleClicks(el, function(e) {
        if (window.frameElement && window.frameElement.squatchJsApi) {
          window.frameElement.squatchJsApi.close();
        }
      });
    });

    // Popup stuff
    each(document.querySelectorAll('[data-open-panel]'), function(el) {
      var element = document.getElementById(el.dataset.openPanel.slice(1));

      if (element) {
        el.onclick = function() {
          my_addClass(element, 'open');
        };
      }
    });

    each(document.querySelectorAll('[data-close-panel]'), function(el) {
      var element = document.getElementById(el.dataset.closePanel.slice(1));

      if (element) {
        el.onclick = function() {
          my_removeClass(element, 'open');
        };
      }
    });

    var setContainerHeight = function(containerEl) {
      var bodyEl           = document.getElementsByClassName('squatch-body')[0];
      var titleEl          = document.getElementsByClassName('squatch-title')[0];
      var titleStyle       = getComputedStyle(titleEl);
      var panelEl          = document.getElementById('squatch-panel');
      var referralsEl      = document.getElementsByClassName('squatch-referrals')[0];
      var referralsTitleEl = document.getElementsByClassName('squatch-referrals-title')[0];

      var bodyHeight = bodyEl.offsetHeight;
      var bodyHeightWithoutTitle = bodyHeight - titleEl.offsetHeight - parseInt(titleStyle.marginTop || '0') - parseInt(titleStyle.marginBottom || '0') - titleEl.offsetTop;
      var panelHeight = panelEl ? panelEl.offsetHeight : 0;

      if (referralsEl && referralsEl.style.display !== 'none') {
        panelHeight -= referralsEl.offsetHeight;
      }

      if (referralsTitleEl && referralsTitleEl.style.display !== 'none') {
        panelHeight -= referralsTitleEl.offsetHeight;
      }

      containerEl.style.height = bodyHeight + panelHeight + "px";

      var stylesheet = document.createElement('style');
      stylesheet.type = 'text/css';

      var css = '#squatch-panel.open {' +
        '-webkit-transform: translate(0, -' + bodyHeightWithoutTitle + 'px);' +
        '-ms-transform: translate(0, -' + bodyHeightWithoutTitle + 'px);' +
        '-o-transform: translate(0, -' + bodyHeightWithoutTitle + 'px);' +
        'transform: translate(0, -' + bodyHeightWithoutTitle + 'px);' +
        '}' +
        'html.lt-ie9 #squatch-panel.open {' +
        'top: -' + bodyHeightWithoutTitle + 'px;' +
        '}';

      if (stylesheet.styleSheet){
        // IE
        stylesheet.styleSheet.cssText = css;
      } else {
        // W3C Standard
        stylesheet.appendChild(document.createTextNode(css));
      }

      document.querySelector('head').appendChild(stylesheet);
    };

    var containerEl = document.getElementsByClassName('squatch-container-popup')[0];

    if (containerEl) {
     window.onload = function() {
       setContainerHeight(containerEl);
     }

    }
  });

})();
<commit_msg>EM - morp sync<commit_after>(function() {
  'use strict';

  domready(function() {

    var scrollElements = document.querySelectorAll('[data-scroll-element]');

    emailFormHandler();
    facebookHandler();
    twitterHandler();
    emailHandler();

    var inValidRange = function(offset, limit) {
      return offset >= 0 && offset < limit;
    };

    var setVisibility = function(element, nextOffset, limit) {
      if(inValidRange(nextOffset, limit)) {
        my_removeClass(element, 'disabled');
      } else {
        my_addClass(element, 'disabled');
      }
    };

    var setVisibilityAll = function(elements, newOffset) {
      var
        scrollElement,
        increment,
        nextOffset,
        limit;

      each(elements, function(el) {
        if (isDOMElement(el)) {
          scrollElement = document.querySelector(el.dataset.scrollElement);
          increment  = parseInt(el.dataset.scrollIncrement);
          nextOffset = newOffset + increment;
          limit      = parseInt(scrollElement.dataset.scrollLimit);

          setVisibility(el, nextOffset, limit);
        }
      });
    };

    var resetScroll = function(element) {
      element.scrollTop = 0;
      element.dataset.scrollOffset = 0;
    };

    each(document.querySelectorAll('[data-clipboard-target]'), function(el) {
      if (isDOMElement(el)) {
        try {
          var clipboard = new Clipboard(el);
          var notification;

          var notify = function(clipboardNotification, notificationText) {
            notification = document.getElementById(clipboardNotification.slice(1));
            notification.textContent = notificationText;
            my_addClass(notification, 'in');
            setTimeout(function() {
              my_removeClass(notification, 'in');
            }, 1400);
          };

          var notifySuccess = function(e) {
            notify(e.trigger.dataset.clipboardNotification, "Copied!");
          };

          var notifyFailure = function(e) {
            //if the copy function failed the text should still be selected, so just ask the user to hit ctrl+c
            notify(e.trigger.dataset.clipboardNotification, "Press Ctrl+C to copy");
          };

          clipboard.on('success', notifySuccess);
          clipboard.on('error', notifyFailure);
          handleClicks(el, function(e) {
            if (window.frameElement && window.frameElement.squatchJsApi) {
              window.frameElement.squatchJsApi._shareEvent(window.squatch, 'DIRECT');
            }
          });
        } catch(err) {}
      }
    });

    each(scrollElements, function(el) {
      if (isDOMElement(el)) {
        var element = document.querySelector(el.dataset.scrollElement);
        var increment = parseInt(el.dataset.scrollIncrement);
        var limit     = parseInt(element.dataset.scrollLimit.valueOf());
        var offset    = parseInt(element.dataset.scrollOffset.valueOf());
        var newOffset;

        element.dataset.scrollLimit = limit;

        var nextOffset = offset + increment;

        setVisibility(el, nextOffset, limit);

        // Force IE to forget previous scroll top value
        resetScroll(element);

        listenToClick(el, 'click', function() {
          offset = parseInt(element.dataset.scrollOffset);

          newOffset = offset + increment;

          if (inValidRange(newOffset, limit)) {
            scrollTop(element, document.getElementById(newOffset).offsetTop, 400);
            element.dataset.scrollOffset = newOffset;

            setVisibilityAll(scrollElements, newOffset);
          }
        });
      }
    });

    each(document.querySelectorAll('[data-moment]'), function(el) {
      if (isDOMElement(el)) {
        var time = moment(parseInt(el.textContent));
        el.textContent = time.fromNow();
      }
    });

    each(document.getElementsByClassName('squatch-header-close'), function(el) {
      if (isDOMElement(el)) {
        handleClicks(el, function(e) {
          if (window.frameElement && window.frameElement.squatchJsApi) {
            window.frameElement.squatchJsApi.close();
          }
        });
      }
    });

    // Popup stuff
    each(document.querySelectorAll('[data-open-panel]'), function(el) {
      if (isDOMElement(el)) {
        var element = document.getElementById(el.dataset.openPanel.slice(1));
        if (element) {
          el.onclick = function() {
            my_addClass(element, 'open');
          };
        }
      }
    });

    each(document.querySelectorAll('[data-close-panel]'), function(el) {
      if (isDOMElement(el)) {
        var element = document.getElementById(el.dataset.closePanel.slice(1));
        if (element) {
          el.onclick = function() {
            my_removeClass(element, 'open');
          };
        }
      }
    });

    var setContainerHeight = function(containerEl) {
      var bodyEl           = document.getElementsByClassName('squatch-body')[0];
      var titleEl          = document.getElementsByClassName('squatch-title')[0];
      var titleStyle       = getComputedStyle(titleEl);
      var panelEl          = document.getElementById('squatch-panel');
      var referralsEl      = document.getElementsByClassName('squatch-referrals')[0];
      var referralsTitleEl = document.getElementsByClassName('squatch-referrals-title')[0];

      var bodyHeight = bodyEl.offsetHeight;
      var bodyHeightWithoutTitle = bodyHeight - titleEl.offsetHeight - parseInt(titleStyle.marginTop || '0') - parseInt(titleStyle.marginBottom || '0') - titleEl.offsetTop;
      var panelHeight = panelEl ? panelEl.offsetHeight : 0;

      if (referralsEl && referralsEl.style.display !== 'none') {
        panelHeight = panelHeight - referralsEl.offsetHeight;
      }

      if (referralsTitleEl && referralsTitleEl.style.display !== 'none') {
        panelHeight = panelHeight - referralsTitleEl.offsetHeight;
      }

      containerEl.style.height = bodyHeight + panelHeight + "px";

      var stylesheet = document.createElement('style');
      stylesheet.type = 'text/css';

      var css = '#squatch-panel.open {' +
        '-webkit-transform: translate(0, -' + bodyHeightWithoutTitle + 'px);' +
        '-ms-transform: translate(0, -' + bodyHeightWithoutTitle + 'px);' +
        '-o-transform: translate(0, -' + bodyHeightWithoutTitle + 'px);' +
        'transform: translate(0, -' + bodyHeightWithoutTitle + 'px);' +
        '}' +
        'html.lt-ie9 #squatch-panel.open {' +
        'top: -' + bodyHeightWithoutTitle + 'px;' +
        '}';

      if (stylesheet.styleSheet){
        // IE
        stylesheet.styleSheet.cssText = css;
      } else {
        // W3C Standard
        stylesheet.appendChild(document.createTextNode(css));
      }

      document.querySelector('head').appendChild(stylesheet);
    };

    var containerEl = document.getElementsByClassName('squatch-container-popup')[0];

    if (containerEl) {
      window.onload = function() {

        var setContainerHeightIfWideEnough = function () {
          var width = window.innerWidth;

          if (width === 500) {
            setContainerHeight(containerEl);
          } else {
            setTimeout(function() {
              setContainerHeightIfWideEnough();
            }, 50);
          }
        };

        setContainerHeightIfWideEnough();
      }
    }
  });

})();
<|endoftext|>


<commit_before>blindApp.controller('IndexController', ['$scope', '$state', 'globals', function($scope, $state, globals) {}])

blindApp.controller('HomeController', ['$scope', '$state', '$http', '$sce', 'globals', function($scope, $state, $http, $sce, globals) {
  document.title = 'blindworm - home'; //set the page title

  $scope.getItems = function( keywords ){
      $http.get('/api/az/' + keywords)
          .then(function(response){
            $scope.items = response.data.ItemSearchResponse.Items;
          });
  };

}])<commit_msg>git er done<commit_after>blindApp.controller('IndexController', ['$scope', '$state', 'globals', function($scope, $state, globals) {}])

blindApp.controller('HomeController', ['$scope', '$state', '$http', '$sce', 'globals', function($scope, $state, $http, $sce, globals) {
  document.title = 'blindworm - home'; //set the page title

  $scope.getItems = function( keywords ){
      $http.get('/api/az/' + keywords)
          .then(function(response){
              console.log(response.data.ItemSearchResponse.Items);
            $scope.items = response.data.ItemSearchResponse.Items;
          });
  };

}])<|endoftext|>