UNPKG

infusion

Version:

Infusion is an application framework for developing flexible stuff with JavaScript

308 lines (284 loc) 12.6 kB
/* Copyright The Infusion copyright holders See the AUTHORS.md file at the top-level directory of this distribution and at https://github.com/fluid-project/infusion/raw/master/AUTHORS.md. Licensed under the Educational Community License (ECL), Version 2.0 or the New BSD license. You may not use this file except in compliance with one these Licenses. You may obtain a copy of the ECL 2.0 License and BSD License at https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt */ var fluid_3_0_0 = fluid_3_0_0 || {}; (function ($, fluid) { "use strict"; fluid.registerNamespace("fluid.moduleLayout"); /** * Calculate the location of the item and the column in which it resides. * @param {Object} - The item. * @param {Object} - The layout object. * @return An object with column index and item index (within that column) properties. * These indices are -1 if the item does not exist in the grid. */ // unsupported - NON-API function fluid.moduleLayout.findColumnAndItemIndices = function (item, layout) { return fluid.find(layout.columns, function (column, colIndex) { var index = $.inArray(item, column.elements); return index === -1 ? undefined : {columnIndex: colIndex, itemIndex: index}; }, {columnIndex: -1, itemIndex: -1}); }; // unsupported - NON-API function fluid.moduleLayout.findColIndex = function (item, layout) { return fluid.find(layout.columns, function (column, colIndex) { return item === column.container ? colIndex : undefined; }, -1); }; /** * Move an item within the layout object. */ // unsupported - NON-API function fluid.moduleLayout.updateLayout = function (item, target, position, layout) { item = fluid.unwrap(item); target = fluid.unwrap(target); var itemIndices = fluid.moduleLayout.findColumnAndItemIndices(item, layout); layout.columns[itemIndices.columnIndex].elements.splice(itemIndices.itemIndex, 1); var targetCol; if (position === fluid.position.INSIDE) { targetCol = layout.columns[fluid.moduleLayout.findColIndex(target, layout)].elements; targetCol.splice(targetCol.length, 0, item); } else { var relativeItemIndices = fluid.moduleLayout.findColumnAndItemIndices(target, layout); targetCol = layout.columns[relativeItemIndices.columnIndex].elements; position = fluid.dom.normalisePosition(position, itemIndices.columnIndex === relativeItemIndices.columnIndex, relativeItemIndices.itemIndex, itemIndices.itemIndex); var relative = position === fluid.position.BEFORE ? 0 : 1; targetCol.splice(relativeItemIndices.itemIndex + relative, 0, item); } }; /** * Builds a layout object from a set of columns and modules. * @param {jQuery} container - The container element. * @param {jQuery} columns - One or more jQuery objects representing columns of data. * @param {jQuery} portlets - One or more "portlet" elements to include in the layout. * @return {Object} - A layout object. */ fluid.moduleLayout.layoutFromFlat = function (container, columns, portlets) { var layout = {}; layout.container = container; layout.columns = fluid.transform(columns, function (column) { return { container: column, elements: fluid.makeArray(portlets.filter(function () { // is this a bug in filter? would have expected "this" to be 1st arg return fluid.dom.isContainer(column, this); })) }; }); return layout; }; /* * Builds a layout object from a serialisable "layout" object consisting of id lists */ fluid.moduleLayout.layoutFromIds = function (idLayout) { return { container: fluid.byId(idLayout.id), columns: fluid.transform(idLayout.columns, function (column) { return { container: fluid.byId(column.id), elements: fluid.transform(column.children, fluid.byId) }; }) }; }; /* * Serializes the current layout into a structure of ids */ fluid.moduleLayout.layoutToIds = function (idLayout) { return { id: fluid.getId(idLayout.container), columns: fluid.transform(idLayout.columns, function (column) { return { id: fluid.getId(column.container), children: fluid.transform(column.elements, fluid.getId) }; }) }; }; fluid.moduleLayout.defaultOnShowKeyboardDropWarning = function (item, dropWarning) { if (dropWarning) { var offset = $(item).offset(); dropWarning = $(dropWarning); dropWarning.css("position", "absolute"); dropWarning.css("top", offset.top); dropWarning.css("left", offset.left); } }; /* * Module Layout Handler for reordering content modules. * * General movement guidelines: * * - Arrowing sideways will always go to the top (moveable) module in the column * - Moving sideways will always move to the top available drop target in the column * - Wrapping is not necessary at this first pass, but is ok */ fluid.defaults("fluid.moduleLayoutHandler", { gradeNames: ["fluid.layoutHandler"], orientation: fluid.orientation.VERTICAL, containerRole: fluid.reorderer.roles.REGIONS, selectablesTabindex: -1, sentinelize: true, events: { onMove: "{reorderer}.events.onMove", onRefresh: "{reorderer}.events.onRefresh", onShowKeyboardDropWarning: "{reorderer}.events.onShowKeyboardDropWarning" }, listeners: { "onShowKeyboardDropWarning.setPosition": "fluid.moduleLayout.defaultOnShowKeyboardDropWarning", onRefresh: { priority: "first", listener: "{that}.computeLayout" }, onMove: { priority: "last", listener: "fluid.moduleLayout.onMoveListener", args: ["{arguments}.0", "{arguments}.1", "{that}.layout"] } }, members: { layout: { expander: { func: "{that}.computeLayout" } }, getRelativePosition: { // TODO: an old-fashioned function member - convert to invoker expander: { funcName: "fluid.reorderer.relativeInfoGetter", args: [ "{that}.options.orientation", fluid.reorderer.WRAP_LOCKED_STRATEGY, fluid.reorderer.GEOMETRIC_STRATEGY, "{that}.dropManager", "{that}.options.disableWrap"] } } }, invokers: { // Use very specific arguments for selectors to avoid circularity // also, do not share our DOM binder for our own selectors with parent, to avoid inability to // update DOM binder's selectors after initialisation - and since we require a DOM binder in order to compute // the modified selectors for upward injection computeLayout: { funcName: "fluid.moduleLayout.computeLayout", args: ["{that}", "{reorderer}.options.selectors.modules", "{that}.dom"] }, computeModules: { // guarantees to read "layout" on every call funcName: "fluid.moduleLayout.computeModules", args: ["{that}.layout", "{that}.isLocked", "{arguments}.0"] }, makeComputeModules: { // expander function to create DOM locators funcName: "fluid.moduleLayout.makeComputeModules", args: ["{that}", "{arguments}.0"] }, isLocked: { funcName: "fluid.moduleLayout.isLocked", args: ["{arguments}.0", "{reorderer}.options.selectors.lockedModules", "{that}.reordererDom"] }, getGeometricInfo: "fluid.moduleLayout.getGeometricInfo({that})", getModel: "fluid.moduleLayout.getModel({that})" }, selectors: { modules: "{reorderer}.options.selectors.modules", columns: "{reorderer}.options.selectors.columns" }, distributeOptions: { target: "{reorderer}.options", record: { selectors: { movables: { expander: { func: "{that}.makeComputeModules", args: [false] } }, dropTargets: { expander: { func: "{that}.makeComputeModules", args: [false] } }, selectables: { expander: { func: "{that}.makeComputeModules", args: [true] } } } } } }); fluid.moduleLayout.getGeometricInfo = function (that) { var options = that.options; var extents = []; var togo = {extents: extents, sentinelize: options.sentinelize}; togo.elementMapper = function (element) { return that.isLocked(element) ? "locked" : null; }; togo.elementIndexer = function (element) { var indices = fluid.moduleLayout.findColumnAndItemIndices(element, that.layout); return { index: indices.itemIndex, length: that.layout.columns[indices.columnIndex].elements.length, moduleIndex: indices.columnIndex, moduleLength: that.layout.columns.length }; }; for (var col = 0; col < that.layout.columns.length; col++) { var column = that.layout.columns[col]; var thisEls = { orientation: options.orientation, elements: fluid.makeArray(column.elements), parentElement: column.container }; // fluid.log("Geometry col " + col + " elements " + fluid.dumpEl(thisEls.elements) + " isLocked [" + // fluid.transform(thisEls.elements, togo.elementMapper).join(", ") + "]"); extents.push(thisEls); } return togo; }; fluid.moduleLayout.getModel = function (that) { return fluid.moduleLayout.layoutToIds(that.layout); // note that that.layout is a "volatile member" }; fluid.moduleLayout.computeLayout = function (that, modulesSelector, dom) { var togo; if (modulesSelector) { togo = fluid.moduleLayout.layoutFromFlat(that.container, dom.locate("columns"), dom.locate("modules")); } if (!togo) { // TODO: this branch appears to be unspecified and untested var idLayout = fluid.get(that.options, "moduleLayout.layout"); togo = fluid.moduleLayout.layoutFromIds(idLayout); } that.layout = togo; return togo; }; fluid.moduleLayout.computeModules = function (layout, isLocked, all) { var modules = fluid.accumulate(layout.columns, function (column, list) { return list.concat(column.elements); // note that concat will not work on a jQuery }, []); if (!all) { fluid.remove_if(modules, isLocked); } return modules; }; fluid.moduleLayout.makeComputeModules = function (that, all) { return function () { return that.computeModules(all); }; }; fluid.moduleLayout.isLocked = function (item, lockedModulesSelector, dom) { var lockedModules = lockedModulesSelector ? dom.fastLocate("lockedModules") : []; return $.inArray(item, lockedModules) !== -1; }; fluid.moduleLayout.onMoveListener = function (item, requestedPosition, layout) { fluid.moduleLayout.updateLayout(item, requestedPosition.element, requestedPosition.position, layout); }; })(jQuery, fluid_3_0_0);