/** * mag */ (function (root, factory) { 'use strict'; // eslint-disable-line semi var name = 'Mag' if (typeof define === 'function' && define.amd) { define(['./mag-analytics'], function (MagnificentAnalytics) { return (root[name] = factory(MagnificentAnalytics)) }) } else if (typeof exports === 'object') { module.exports = factory(require('./mag-analytics')) } else { root[name] = factory(root.MagnificentAnalytics) } }(this, function (MagnificentAnalytics) { 'use strict'; // eslint-disable-line semi /** * @typedef {Object} MagModelFocus * @property {number} x - X position, from [0,1]. * @property {number} y - Y position, from [0,1]. */ /** * @typedef {Object} MagModelLens * @property {number} w - Width, from (0,∞). * @property {number} h - Height, from (0,∞). */ /** * @typedef {Object} MagModel * @property {number} zoom - Zoom level, from (0,∞). * @property {MagModelFocus} focus - Focus object. * @property {MagModelLens} lens - Lens object. */ /** * @typedef {Object} MagOptions * @property {MagModel} model - A model. * @property {number} zoomMin - Minimum zoom level allowed, from (0,∞). * @property {number} zoomMax - Maximum zoom level allowed, from (0,∞). * @property {boolean} constrainLens - Whether lens position is constrained. * @property {boolean} constrainZoomed - Whether zoomed position is constrained. */ /** * Mag constructor. * * @alias module:mag * * @class * * @param {MagOptions} options - Options. */ var Mag = function (options) { options = options || {} options.model = options.model || {} options.zoomMin = options.zoomMin || 1 options.zoomMax = options.zoomMax || 10 options.constrainLens = options.constrainLens !== false options.constrainZoomed = options.constrainZoomed !== false this.id = options.id this.model = options.model this.options = options this.fillModel() } Mag.prototype.fillXY = function (r) { r = r || {} r.x = r.x || 0 r.y = r.y || 0 return r } Mag.prototype.fillWH = function (r) { r = r || {} r.w = r.w || 0 r.h = r.h || 0 return r } Mag.prototype.fillModel = function () { var model = this.model model.mode = model.mode || 'lag' model.focus = this.fillXY(model.focus) model.lens = this.fillXY(this.fillWH(model.lens)) model.zoomed = this.fillXY(this.fillWH(model.zoomed)) model.boundedLens = this.fillXY(this.fillWH(model.boundedLens)) model.zoom = model.zoom || 1 model.ratio = model.ratio || 1 } /** * Update computed model state, especially lens and zoomed. */ Mag.prototype.compute = function () { var lens, focus, zoomed, zoom, dw, dh var options = this.options var model = this.model lens = model.lens focus = model.focus zoomed = model.zoomed zoom = model.zoom zoom = this.minMax(zoom, options.zoomMin, options.zoomMax) focus.x = this.minMax(focus.x, 0, 1) focus.y = this.minMax(focus.y, 0, 1) dw = 1 / zoom dh = 1 / zoom dh = dh / model.ratio lens.w = dw lens.h = dh if (options.constrainLens) { lens = this.constrainLensWH(lens) } lens.x = focus.x - (lens.w / 2) lens.y = focus.y - (lens.h / 2) if (options.constrainLens) { lens = this.constrainLensXY(lens) } zoomed.w = 1 / dw zoomed.h = 1 / dh var z = this.constrainZoomed(zoomed, options) if (z.w !== zoomed.w) { zoom *= z.w / zoomed.w } zoomed = z zoomed.x = 0.5 - focus.x * zoomed.w zoomed.y = 0.5 - focus.y * zoomed.h // the following is better equation for constrained zoom // zoomed.x = focus.x * (1 - zoom) // zoomed.y = focus.y * (1 - zoom) if (options.constrainZoomed) { zoomed.x = this.minMax(zoomed.x, 1 - zoom, 0) zoomed.y = this.minMax(zoomed.y, 1 - zoom, 0) } model.lens = lens model.focus = focus model.zoomed = zoomed model.zoom = zoom } Mag.prototype.minMax = function (val, min, max) { return val < min ? min : (val > max ? max : val) } Mag.prototype.minMax1 = function (val, min) { return this.minMax(val, min, 1) } Mag.prototype.constrainZoomed = function (r, options) { var wm var hm wm = this.minMax(r.w, options.zoomMin, options.zoomMax) if (wm !== r.w) { hm *= wm / r.w hm = this.minMax(hm, options.zoomMin, options.zoomMax) } else { hm = this.minMax(r.h, options.zoomMin, options.zoomMax) if (hm !== r.h) { wm *= hm / r.h wm = this.minMax(wm, options.zoomMin, options.zoomMax) } } return { w: wm, h: hm, x: r.x, y: r.y } } Mag.prototype.constrainLensWH = function (r) { var wm var hm wm = this.minMax1(r.w, 0.1) if (wm !== r.w) { hm *= wm / r.w hm = this.minMax1(hm, 0.1) } else { hm = this.minMax1(r.h, 0.1) if (hm !== r.h) { wm *= hm / r.h wm = this.minMax1(wm, 0.1) } } return { w: wm, h: hm, x: r.x, y: r.y } } Mag.prototype.constrainLensXY = function (r) { return { x: this.minMax(r.x, 0, 1 - r.w), y: this.minMax(r.y, 0, 1 - r.h), w: r.w, h: r.h } } Mag.prototype.constrainLens = function (r) { var c = this.constrainLensXY(this.constrainLensWH(r)) if (((c.w + c.x) > 1)) { c.x = Math.max(0, 1 - c.w) } if (((c.h + c.y) > 1)) { c.y = Math.max(0, 1 - c.h) } return c } Mag.prototype.project = function (frame) { var model = this.model var lens = model.lens return { x: lens.x * frame.w, y: lens.y * frame.h, w: lens.w * frame.w, h: lens.h * frame.h } } if (MagnificentAnalytics) { MagnificentAnalytics.track('mag.js') } return Mag }))