UNPKG

@oat-sa/tao-test-runner-qti

Version:
895 lines (856 loc) 35.9 kB
define(['jquery', 'lodash', 'core/statifier', 'core/eventifier', 'ui/component', 'ui/component/placeable', 'ui/component/draggable', 'ui/component/resizable', 'ui/component/stackable'], function ($, _, statifier, eventifier, componentFactory, makePlaceable, makeDraggable, makeResizable, makeStackable) { 'use strict'; $ = $ && Object.prototype.hasOwnProperty.call($, 'default') ? $['default'] : $; _ = _ && Object.prototype.hasOwnProperty.call(_, 'default') ? _['default'] : _; statifier = statifier && Object.prototype.hasOwnProperty.call(statifier, 'default') ? statifier['default'] : statifier; eventifier = eventifier && Object.prototype.hasOwnProperty.call(eventifier, 'default') ? eventifier['default'] : eventifier; componentFactory = componentFactory && Object.prototype.hasOwnProperty.call(componentFactory, 'default') ? componentFactory['default'] : componentFactory; makePlaceable = makePlaceable && Object.prototype.hasOwnProperty.call(makePlaceable, 'default') ? makePlaceable['default'] : makePlaceable; makeDraggable = makeDraggable && Object.prototype.hasOwnProperty.call(makeDraggable, 'default') ? makeDraggable['default'] : makeDraggable; makeResizable = makeResizable && Object.prototype.hasOwnProperty.call(makeResizable, 'default') ? makeResizable['default'] : makeResizable; makeStackable = makeStackable && Object.prototype.hasOwnProperty.call(makeStackable, 'default') ? makeStackable['default'] : makeStackable; /** * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; under version 2 * of the License (non-upgradable). * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2017 (original work) Open Assessment Technologies SA; */ var defaultDimensions = { outerWidth: 600, outerHeight: 400, innerWidth: 500, innerHeight: 20 }; var defaultPosition = { outerX: 0, outerY: 0, innerX: 50, innerY: 50 }; var defaultOptions = { dragMinWidth: 10, dragMinHeight: 10, resizeHandleSize: 10, innerDragHeight: 20 }; var stackingOptions = { stackingScope: 'test-runner' }; var constrains; /** * @param {Object} options * @param {Number} options.resizeHandleSize - size of the resize handlers on each resizable edge * @param {Number} options.dragMinWidth - minimal width for the draggable area of each component. * @param {Number} options.dragMinHeight - minimal height for the draggable area of each component. * @param {Number} options.innerDragHeight - height of the inner window drag handle * @param {Object} dimensions * @param {Number} dimensions.outerWidth - overall mask width * @param {Number} dimensions.outerHeight - overall mask height * @param {Number} dimensions.innerWidth - inner window width * @param {Number} dimensions.innerHeight - inner window height * @param {Number} position * @param {Number} position.outerX - overall mask x * @param {Number} position.outerY - overall mask y * @param {Number} position.innerX - inner window mask x * @param {Number} position.innerY - inner window mask y */ function compoundMaskFactory(options, dimensions, position) { var compoundMask, allParts = {}, innerDrag, closer, visualGuides = {}; /** * ============================================ * Definition of Mask & Overlay component types * ============================================ */ /** * Create a mask component. They are used for masking (obviously) but also resizing the compound mask * @param {Object} maskConfig * @param {String} maskConfig.id * @param {Function} maskConfig.place - size and position the mask according to the transform model * @param {Function} maskConfig.placeOverlay - size and position the overlay according to the transform model * @param {Function} maskConfig.beforeResize - used to set the resize limit depending on which edge the resizing occurs * @param {Function} maskConfig.onResize - how the resize affect the transform model * @param {Object} maskConfig.edges - Interact configuration to specify which edges can be used for resizing * @param {Number} maskConfig.minWidth * @param {Number} maskConfig.minHeight */ function createMask(maskConfig) { var mask, maskAPI = { place: maskConfig.place, placeOverlay: maskConfig.placeOverlay, styleResizableEdges: function styleResizableEdges() { var $element = this.getElement(); _.forOwn(this.config.edgesBorders, function (isResizable, edgeId) { if (isResizable) { $element.addClass(`border-${edgeId}`); } }); }, addResizeControll: function addResizeControll() { var $element = this.getElement(); var $resizeControll = $('<div>', { class: 'resize-control' }); $element.append($resizeControll); } }; mask = componentFactory(maskAPI, maskConfig); makeResizable(mask); makeStackable(mask, stackingOptions); return mask.on('render', function () { var $element = this.getElement(); this.styleResizableEdges(); if (this.config.resizeControll) { this.addResizeControll(); } $element.addClass(`line-reader-mask ${maskConfig.id}`).on('mousedown touchstart', function () { bringAllToFront(); }); // uncomment this to see what's going on with masks: // $element.css({ border: '1px solid olive'}); }).on('resizestart', function () { innerDrag.hide(); closer.hide(); invokeOnOverlays('hide'); invokeOnMasks('setState', ['resizing', true]); this.setState('resizer', true); }).on('beforeresize', maskConfig.beforeResize || _.noop).on('resize', maskConfig.onResize || _.noop).on('resizeend', function () { applyTransformsToOverlays(); applyTransformsToInnerDrag(); applyTransformsToCloser(); invokeOnMasks('setState', ['resizing', false]); invokeOnOverlays('show'); innerDrag.show(); closer.show(); this.setState('resizer', false); }).init(); } /** * Create a overlay component. Overlay are invisible and are used for dragging. * When clicked, the whole mask is hidden and only the overlay is displayed, after being resized to fit the whole mask surface * this allows for performance improvement as well as giving the dragged element proper dragging boundaries. * Also, visual guides are added, like a fake inner window, during the drag * @param {Object} overlayConfig * @param {String} overlayConfig.id */ function createOverlay(overlayConfig) { var overlay, overlayAPI = { transformOverlay: function transformOverlay() { var $element = this.getElement(); this._sizeBackup = this.getSize(); this._posBackup = this.getPosition(); this.setSize(dimensions.outerWidth, dimensions.outerHeight).moveTo(position.outerX, position.outerY); $element.addClass('moving'); this.setState('transformed', true); }, restoreOverlay: function restoreOverlay() { var $element = this.getElement(); if (this.is('transformed')) { this.setSize(this._sizeBackup.width, this._sizeBackup.height).moveTo(this._posBackup.x, this._posBackup.y); $element.removeClass('moving'); this._sizeBackup = null; this._posBackup = null; this.setState('transformed', false); } }, appendVisualGuides: function appendVisualGuides() { var $element = this.getElement(), borderWidth = 1, // this mirror the $lrBorderWidth css variable borderOffset = borderWidth * 2; $element.append(visualGuides.$maskBg); $element.append(visualGuides.$innerWindow); visualGuides.$maskBg.css({ width: dimensions.outerWidth - borderOffset, height: dimensions.outerHeight - borderOffset, 'border-top-width': dimensions.topHeight - borderOffset, 'border-right-width': dimensions.rightWidth - borderOffset, 'border-bottom-width': dimensions.bottomHeight - borderOffset, 'border-left-width': dimensions.leftWidth - borderOffset }); visualGuides.$innerWindow.css({ width: dimensions.innerWidth, height: dimensions.innerHeight, left: dimensions.leftWidth - borderOffset, top: dimensions.topHeight - borderOffset }); }, removeVisualGuides: function removeVisualGuides() { visualGuides.$maskBg.remove(); visualGuides.$innerWindow.remove(); } }; overlay = componentFactory(overlayAPI, overlayConfig); makeDraggable(overlay); makeStackable(overlay, stackingOptions); return overlay.on('render', function () { var self = this, $element = this.getElement(), // captures touch and mouse // also fixes issue with IE not capturing 'mousedown' etc pointerEventsPrefix = window.PointerEvent ? 'pointer' : 'mouse', $moveIcon = $('<div>', { class: 'icon icon-mobile-menu' }); $element.addClass(`line-reader-overlay ${overlayConfig.id}`).on(`${pointerEventsPrefix}down` + ` touchstart`, function () { bringAllToFront(); self.transformOverlay(); }).on(`${pointerEventsPrefix}up` + ` touchend`, function () { self.restoreOverlay(); }).prepend($moveIcon); }).on('dragstart', function () { innerDrag.hide(); closer.hide(); invokeOnMasks('hide'); this.appendVisualGuides(); }).on('dragmove', function moveAllPartsTogether(xOffsetRelative, yOffsetRelative) { // update the transform model position.outerX += xOffsetRelative; position.outerY += yOffsetRelative; position.innerX += xOffsetRelative; position.innerY += yOffsetRelative; }).on('dragend', function () { this.removeVisualGuides(); // although they are already display, calling show() again on the overlays // will force their z-Index at the top of the stack invokeOnAll('show'); innerDrag.show(); closer.show(); // apply the new transform model applyTransforms(); }).init(); } /** * ========================== * Inner Drag Handle & Closer * ========================== */ /** * This handle allows to drag the inner window */ function createInnerDragHandle() { // uncomment this (and a few lines below) if debugging is needed: // var $boundingBox = $('<div>').css({ position: 'fixed', 'box-sizing': 'border-box', border: '1px solid red' }); innerDrag = componentFactory(); makeStackable(innerDrag, stackingOptions); makeDraggable(innerDrag, { dragRestriction: function dragRestriction() { var fixedXY = allParts.nw.mask.getElement().offset(), rect; rect = { x: fixedXY.left + constrains.minWidth, y: fixedXY.top + (constrains.minTopHeight + dimensions.innerHeight + options.resizeHandleSize), width: dimensions.outerWidth - constrains.minWidth * 2, height: dimensions.outerHeight - (dimensions.innerHeight + constrains.minTopHeight + constrains.minBottomHeight - options.innerDragHeight) }; // uncomment to see what's going on: // allParts.ne.mask.getContainer().append($boundingBox); // $boundingBox.css({ width: rect.width, height: rect.height, top: rect.y, left: rect.x }); return rect; } }).on('render', function () { var $element = this.getElement(), $dragIcon = $('<div>', { class: 'icon icon-move' }); $element.css('touch-action', 'none'); $element.addClass('line-reader-inner-drag'); $element.css({ background: 'none' }); $element.append($dragIcon); }).on('dragstart', function () { closer.hide(); bringAllToFront(); invokeOnMasks('setState', ['resizing', true]); }).on('dragmove', function (xOffsetRelative, yOffsetRelative) { position.innerX += xOffsetRelative; position.innerY += yOffsetRelative; dimensions.leftWidth += xOffsetRelative; dimensions.topHeight += yOffsetRelative; dimensions.rightWidth -= xOffsetRelative; dimensions.bottomHeight -= yOffsetRelative; applyTransformsToMasks(); }).on('dragend', function () { innerDrag.bringToFront(); closer.show(); invokeOnMasks('setState', ['resizing', false]); applyTransformsToOverlays(); }).init(); } /** * Close button for the compound mask */ function createCloser() { closer = componentFactory(); makeStackable(closer, stackingOptions); makePlaceable(closer).on('render', function () { var self = this, $element = this.getElement(), $closeIcon = $('<div>', { class: 'icon icon-result-nok' }); $element.append($closeIcon); $element.addClass('line-reader-closer'); $element.on('mousedown touchstart', function () { bringAllToFront(); }); $element.on('click', function (e) { e.stopPropagation(); self.trigger('click'); }); }).init(); } /** * ================= * Utility functions * ================= */ function bringAllToFront() { invokeOnAll('bringToFront'); innerDrag.bringToFront(); closer.bringToFront(); } function invokeOnAll(fn, args) { invokeOnMasks(fn, args); invokeOnOverlays(fn, args); } function invokeOnMasks(fn, args) { invokeOn('mask', fn, args); } function invokeOnOverlays(fn, args) { invokeOn('overlay', fn, args); } /** * Invoke a method on all compound mask parts, whether mask or overlays * @param {String} target - mask | overlay * @param {String} fn - the name of the method to invoke * @param {*[]} args - arguments passed on invoke */ function invokeOn(target, fn, args) { _.forOwn(allParts, function (part) { if (_.isObject(part[target]) && _.isFunction(part[target][fn])) { part[target][fn](...(args || [])); } }); } /** * ================================= * Transform model related functions * ================================= */ function applyTransforms() { applyTransformsToMasks(); applyTransformsToOverlays(); applyTransformsToInnerDrag(); applyTransformsToCloser(); } function applyTransformsToMasks() { invokeOnMasks('place'); } function applyTransformsToOverlays() { _.forOwn(allParts, function (part) { if (part.overlay) { part.mask.placeOverlay(part.overlay); } }); } function applyTransformsToInnerDrag() { if (innerDrag) { innerDrag.setSize(dimensions.innerWidth - 20, options.innerDragHeight).moveTo(position.innerX + 10, position.innerY + dimensions.innerHeight + options.resizeHandleSize); } } function applyTransformsToCloser() { if (closer) { closer.setSize(constrains.minWidth - options.resizeHandleSize, constrains.minHeight - options.resizeHandleSize).moveTo(position.outerX + dimensions.outerWidth - constrains.minWidth - 5, // manual adjustment so it looks better position.outerY + options.resizeHandleSize - 4); } } /** * Check that the given transform model respect the current constrains. * If not, correct them */ function correctTransforms() { if (dimensions.topHeight < constrains.minTopHeight) { dimensions.topHeight = constrains.minTopHeight; position.innerY = position.outerY + constrains.minTopHeight; } if (dimensions.innerHeight < constrains.minHeight) { dimensions.innerHeight = constrains.minHeight; } if (dimensions.bottomHeight < constrains.minBottomHeight) { dimensions.bottomHeight = constrains.minBottomHeight; } dimensions.outerHeight = dimensions.topHeight + dimensions.innerHeight + dimensions.bottomHeight; if (dimensions.leftWidth < constrains.minWidth) { dimensions.leftWidth = constrains.minWidth; position.innerX = position.outerX + constrains.minWidth; } if (dimensions.innerWidth < constrains.minWidth) { dimensions.innerWidth = constrains.minWidth; } if (dimensions.rightWidth < constrains.minWidth) { dimensions.rightWidth = constrains.minWidth; } dimensions.outerWidth = dimensions.leftWidth + dimensions.innerWidth + dimensions.rightWidth; } /** * Update the transform model during a resize affecting the top height * @param {Number} newHeight * @param {Number} newY * @param {Boolean} fromTop - if the resize occurs from the top */ function setTopHeight(newHeight, newY, fromTop) { dimensions.topHeight = newHeight; if (fromTop) { dimensions.outerHeight = newHeight + dimensions.innerHeight + dimensions.bottomHeight; position.outerY = newY; } else { dimensions.innerHeight = dimensions.outerHeight - newHeight - dimensions.bottomHeight; position.innerY = position.outerY + newHeight; } } /** * Update the transform model during a resize affecting the right width * @param {Number} newWidth * @param {Number} newX * @param {Boolean} fromLeft - if the resize occurs from the left */ function setRightWidth(newWidth, newX, fromLeft) { dimensions.rightWidth = newWidth; if (fromLeft) { dimensions.innerWidth = newX - position.innerX; } else { dimensions.outerWidth = dimensions.leftWidth + dimensions.innerWidth + newWidth; } } /** * Update the transform model during a resize affecting the bottom height * @param {Number} newHeight * @param {Number} newY * @param {Boolean} fromTop - if the resize occurs from the top */ function setBottomHeight(newHeight, newY, fromTop) { dimensions.bottomHeight = newHeight; if (fromTop) { dimensions.innerHeight = newY - position.innerY; dimensions.bottomHeight = newHeight; } else { dimensions.outerHeight = dimensions.topHeight + dimensions.innerHeight + newHeight; } } /** * Update the transform model during a resize affecting the left width * @param {Number} newWidth * @param {Number} newX * @param {Boolean} fromLeft - if the resize occurs from the left */ function setLeftWidth(newWidth, newX, fromLeft) { dimensions.leftWidth = newWidth; if (fromLeft) { dimensions.outerWidth = newWidth + dimensions.innerWidth + dimensions.rightWidth; position.outerX = newX; } else { dimensions.innerWidth = dimensions.outerWidth - newWidth - dimensions.rightWidth; position.innerX = position.outerX + newWidth; } } /** * Update the transform model during a resize affecting inner height * @param {Number} newHeight */ const setInnerHeight = newHeight => { dimensions.innerHeight = newHeight; dimensions.bottomHeight = dimensions.outerHeight - dimensions.innerHeight - dimensions.topHeight; }; /** * ====================================== * Mask parts and other elements creation * ====================================== */ function createCompoundMask() { // North createPart({ id: 'n', edges: { top: false, right: false, bottom: false, left: false }, edgesBorders: { top: true, right: false, bottom: true, left: false }, addOverlay: true, minHeight: constrains.minTopHeight, // move and dimension the mask place: function place() { this.moveTo(position.innerX, position.outerY).setSize(dimensions.innerWidth, dimensions.topHeight); }, // move and dimension the overlay placeOverlay: function placeOverlay(overlay) { var pos = this.getPosition(), size = this.getSize(); overlay.moveTo(position.outerX, pos.y).setSize(dimensions.outerWidth, size.height); }, // set a resize limit whenever resize happens on an inner edge (here, the top inner edge, eg. the bottom of the mask), // so the min/max width/height limit for "inner component" is respected beforeResize: function beforeResize(width, height, fromLeft, fromTop) { this.config.maxHeight = fromTop ? null : dimensions.topHeight + (dimensions.innerHeight - constrains.minHeight); }, // set the new transform values (dimension and position) resulting from the current mask resize, and apply them onResize: function onResize(width, height, fromLeft, fromTop, x, y) { setTopHeight(height, y, fromTop); applyTransformsToMasks(); } }); // North-east createPart({ id: 'ne', edges: { top: false, right: false, bottom: false, left: false }, edgesBorders: { top: true, right: true, bottom: false, left: false }, minHeight: constrains.minTopHeight, place: function place() { this.moveTo(position.innerX + dimensions.innerWidth, position.outerY).setSize(dimensions.rightWidth, dimensions.topHeight); }, placeOverlay: function placeOverlay(overlay) { var pos = this.getPosition(), size = this.getSize(); overlay.moveTo(pos.x, pos.y + options.resizeHandleSize).setSize(size.width - options.resizeHandleSize, size.height - options.resizeHandleSize * 2); }, onResize: function onResize(width, height, fromLeft, fromTop, x, y) { setTopHeight(height, y, fromTop); setRightWidth(width, x, fromLeft); applyTransformsToMasks(); } }); // South east createPart({ id: 'se', edges: { top: false, right: '.resize-control', bottom: '.resize-control', left: false }, edgesBorders: { top: false, right: true, bottom: true, left: false }, minHeight: constrains.minBottomHeight, resizeControll: true, place: function place() { this.moveTo(position.innerX + dimensions.innerWidth, position.innerY + dimensions.innerHeight).setSize(dimensions.rightWidth, dimensions.bottomHeight); }, placeOverlay: function placeOverlay(overlay) { var pos = this.getPosition(), size = this.getSize(); overlay.moveTo(pos.x, pos.y + options.resizeHandleSize).setSize(size.width - options.resizeHandleSize, size.height - options.resizeHandleSize * 2); }, onResize: function onResize(width, height, fromLeft, fromTop, x, y) { setRightWidth(width, x, fromLeft); setBottomHeight(height, y, fromTop); applyTransformsToMasks(); } }); // South createPart({ id: 's', edges: { top: false, right: false, bottom: false, left: false }, edgesBorders: { top: true, right: false, bottom: true, left: false }, minHeight: constrains.minBottomHeight, place: function place() { this.moveTo(position.innerX, position.innerY + dimensions.innerHeight).setSize(dimensions.innerWidth, dimensions.bottomHeight); }, placeOverlay: function placeOverlay(overlay) { var pos = this.getPosition(), size = this.getSize(); overlay.moveTo(pos.x, pos.y + options.resizeHandleSize).setSize(size.width, size.height - options.resizeHandleSize * 2); }, beforeResize: function beforeResize(width, height, fromLeft, fromTop) { this.config.maxHeight = fromTop ? dimensions.bottomHeight + (dimensions.innerHeight - constrains.minHeight) : null; }, onResize: function onResize(width, height, fromLeft, fromTop, x, y) { setBottomHeight(height, y, fromTop); applyTransformsToMasks(); } }); // East createPart({ id: 'e', edges: { top: false, right: false, bottom: '.resize-control', left: '.resize-control' }, edgesBorders: { top: false, right: true, bottom: false, left: true }, resizeControll: true, place: function place() { this.moveTo(position.innerX + dimensions.innerWidth, position.innerY).setSize(dimensions.rightWidth, dimensions.innerHeight); }, placeOverlay: function placeOverlay(overlay) { var pos = this.getPosition(), size = this.getSize(); overlay.moveTo(pos.x + options.resizeHandleSize, pos.y - options.resizeHandleSize).setSize(size.width - options.resizeHandleSize * 2, size.height + options.resizeHandleSize * 2); }, // eslint-disable-next-line no-unused-vars beforeResize: function beforeResize(width, height, fromLeft) { this.config.maxWidth = dimensions.rightWidth + (dimensions.innerWidth - constrains.minWidth); this.config.minWidth = constrains.minWidth; this.config.maxHeight = dimensions.outerHeight - dimensions.topHeight - constrains.minBottomHeight; }, onResize: function onResize(width, height, fromLeft, fromTop, x) { setRightWidth(width, x, fromLeft); setInnerHeight(height); applyTransformsToMasks(); } }); // South-west createPart({ id: 'sw', edges: { top: false, right: false, bottom: false, left: false }, edgesBorders: { top: false, right: false, bottom: true, left: true }, minHeight: constrains.minBottomHeight, place: function place() { this.moveTo(position.outerX, position.innerY + dimensions.innerHeight).setSize(dimensions.leftWidth, dimensions.bottomHeight); }, placeOverlay: function placeOverlay(overlay) { var pos = this.getPosition(), size = this.getSize(); overlay.moveTo(pos.x + options.resizeHandleSize, pos.y + options.resizeHandleSize).setSize(size.width - options.resizeHandleSize, size.height - options.resizeHandleSize * 2); }, onResize: function onResize(width, height, fromLeft, fromTop, x, y) { setBottomHeight(height, y, fromTop); setLeftWidth(width, x, fromLeft); applyTransformsToMasks(); } }); // West createPart({ id: 'w', edges: { top: false, right: false, bottom: false, left: false }, edgesBorders: { top: false, right: true, bottom: false, left: true }, place: function place() { this.moveTo(position.outerX, position.innerY).setSize(dimensions.leftWidth, dimensions.innerHeight); }, placeOverlay: function placeOverlay(overlay) { var pos = this.getPosition(), size = this.getSize(); overlay.moveTo(pos.x + options.resizeHandleSize, pos.y - options.resizeHandleSize).setSize(size.width - options.resizeHandleSize * 2, size.height + options.resizeHandleSize * 2); }, beforeResize: function beforeResize(width, height, fromLeft) { this.config.maxWidth = fromLeft ? null : dimensions.leftWidth + (dimensions.innerWidth - constrains.minWidth); }, onResize: function onResize(width, height, fromLeft, fromTop, x) { setLeftWidth(width, x, fromLeft); applyTransformsToMasks(); } }); // North-west createPart({ id: 'nw', edges: { top: false, right: false, bottom: false, left: false }, edgesBorders: { top: true, right: false, bottom: false, left: true }, minHeight: constrains.minTopHeight, place: function place() { this.moveTo(position.outerX, position.outerY).setSize(dimensions.leftWidth, dimensions.topHeight); }, placeOverlay: function placeOverlay(overlay) { var pos = this.getPosition(), size = this.getSize(); overlay.moveTo(pos.x + options.resizeHandleSize, pos.y + options.resizeHandleSize).setSize(size.width - options.resizeHandleSize, size.height - options.resizeHandleSize * 2); }, onResize: function onResize(width, height, fromLeft, fromTop, x, y) { setTopHeight(height, y, fromTop); setLeftWidth(width, x, fromLeft); applyTransformsToMasks(); } }); } function createPart(partConfig) { allParts[partConfig.id] = { mask: createMask(_.assign({}, constrains, partConfig)), overlay: partConfig.addOverlay ? createOverlay(partConfig) : null }; } function createVisualGuides() { visualGuides.$maskBg = $('<div>', { class: 'mask-bg' }); visualGuides.$innerWindow = $('<div>', { class: 'inner-window' }); } /** * ========================= * The compoundMask instance * ========================= */ dimensions = _.defaults(dimensions || {}, defaultDimensions); position = _.defaults(position || {}, defaultPosition); options = _.defaults(options || {}, defaultOptions); constrains = { minWidth: options.resizeHandleSize * 2 + options.dragMinWidth, minHeight: options.resizeHandleSize * 2 + options.dragMinHeight, minBottomHeight: options.resizeHandleSize * 2 + options.innerDragHeight, minTopHeight: options.resizeHandleSize * 2 + 18 // make sure that top will fit header size }; compoundMask = { init: function init() { var self = this; this.setTransforms(dimensions, position); createCompoundMask(); createVisualGuides(); createInnerDragHandle(); createCloser(); closer.on('click', function () { self.hide(); self.trigger('close'); }); return this; }, render: function render($container) { invokeOnAll('render', [$container]); innerDrag.render($container); closer.render($container); applyTransforms(); return this; }, destroy: function destroy() { invokeOnAll('destroy'); visualGuides = null; innerDrag = null; closer = null; return this; }, show: function show() { invokeOnAll('show'); innerDrag.show(); closer.show(); this.setState('hidden', false); return this; }, hide: function hide() { invokeOnAll('hide'); innerDrag.hide(); closer.hide(); this.setState('hidden', true); return this; }, /** * Allow updating the transform model * @param {Object} dim * @param {Number} dim.outerWidth - overall mask width * @param {Number} dim.outerHeight - overall mask height * @param {Number} dim.innerWidth - inner window width * @param {Number} dim.innerHeight - inner window height * @param {Number} pos * @param {Number} pos.outerX - overall mask x * @param {Number} pos.outerY - overall mask y * @param {Number} pos.innerX - inner window x * @param {Number} pos.innerY - inner window y */ setTransforms: function setTransforms(dim, pos) { dimensions = _.defaults(dim || {}, dimensions); position = _.defaults(pos || {}, position); // automatically complete the dimensions dimensions.topHeight = pos.innerY - pos.outerY; dimensions.rightWidth = dim.outerWidth - (pos.innerX - pos.outerX) - dim.innerWidth; dimensions.bottomHeight = dim.outerHeight - (pos.innerY - pos.outerY) - dim.innerHeight; dimensions.leftWidth = pos.innerX - pos.outerX; correctTransforms(); applyTransforms(); }, getDimensions: function getDimensions() { return dimensions; }, getPosition: function getPosition() { return position; }, getParts: function getParts() { return allParts; } }; statifier(compoundMask); eventifier(compoundMask); return compoundMask; } return compoundMaskFactory; });