From febea3e9702bf111a6dc8fa67cf66812bc106e43 Mon Sep 17 00:00:00 2001 From: Nathan Cahill Date: Mon, 8 Oct 2018 22:07:36 -0700 Subject: [PATCH] update build tools --- package.json | 19 +- rollup.config.js | 23 +- split.js | 1047 +++++++++++++++++++++++----------------------- split.min.js | 2 +- 4 files changed, 548 insertions(+), 543 deletions(-) diff --git a/package.json b/package.json index b4ba482c..108691e1 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ }, "homepage": "https://github.com/nathancahill/Split.js#readme", "devDependencies": { - "buble": "^0.15.2", + "buble": "^0.19", "eslint": "^3.19.0", "eslint-config-airbnb": "^14.0.0", "eslint-plugin-compat": "^1.0.2", @@ -28,21 +28,18 @@ "grunt": "1.0.1", "grunt-cli": "1.2.0", "grunt-contrib-jasmine": "1.1.0", - "gzip-size-cli": "^2.0.0", + "gzip-size-cli": "^3.0.0", "jasmine": "2.6.0", - "rollup": "^0.41.6", - "rollup-multiple": "^0.41.6", - "rollup-plugin-buble": "^0.15.0", - "rollup-plugin-uglify": "^2.0.0", - "rollup-watch": "^3.2.2", - "opencollective": "^1.0.3", - "purgecss": "^1.1.0" + "rollup": "^0.66", + "rollup-plugin-buble": "^0.19", + "rollup-plugin-uglify": "^6.0.0", + "opencollective": "^1.0.3" }, "scripts": { "lint": "eslint src", "test": "npm run lint && npm run build && grunt jasmine", - "build": "rollup-multiple -c && npm run size", - "watch": "rollup-multiple -cw", + "build": "rollup -c && npm run size", + "watch": "rollup -cw", "size": "echo \"gzip size: $(gzip-size --raw $npm_package_minified_main) bytes\"", "postinstall": "opencollective postinstall" }, diff --git a/rollup.config.js b/rollup.config.js index 54df7393..f782d529 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,23 +1,28 @@ import buble from 'rollup-plugin-buble' -import uglify from 'rollup-plugin-uglify' +import { uglify } from 'rollup-plugin-uglify' const pkg = require('./package.json') -const common = { - entry: 'src/split.js', +const output = { format: 'umd', - moduleName: 'Split', + file: 'split.js', + name: 'Split', banner: `/*! Split.js - v${pkg.version} */\n`, } -export default [Object.assign({}, common, { - dest: 'split.js', +export default [{ + input: 'src/split.js', + output, plugins: [ buble(), ], -}), Object.assign({}, common, { - dest: 'split.min.js', +}, { + input: 'src/split.js', + output: { + ...output, + file: 'split.min.js', + }, plugins: [ buble(), uglify({ @@ -26,4 +31,4 @@ export default [Object.assign({}, common, { }, }), ], -})] +}] diff --git a/split.js b/split.js index cec5d080..bae63acd 100644 --- a/split.js +++ b/split.js @@ -1,586 +1,589 @@ /*! Split.js - v1.5.0 */ (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : - typeof define === 'function' && define.amd ? define(factory) : - (global.Split = factory()); + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global.Split = factory()); }(this, (function () { 'use strict'; -// The programming goals of Split.js are to deliver readable, understandable and -// maintainable code, while at the same time manually optimizing for tiny minified file size, -// browser compatibility without additional requirements, graceful fallback (IE8 is supported) -// and very few assumptions about the user's page layout. -var global = window; -var document = global.document; - -// Save a couple long function names that are used frequently. -// This optimization saves around 400 bytes. -var addEventListener = 'addEventListener'; -var removeEventListener = 'removeEventListener'; -var getBoundingClientRect = 'getBoundingClientRect'; -var gutterStartDragging = '_a'; -var aGutterSize = '_b'; -var bGutterSize = '_c'; -var HORIZONTAL = 'horizontal'; -var NOOP = function () { return false; }; - -// Figure out if we're in IE8 or not. IE8 will still render correctly, -// but will be static instead of draggable. -var isIE8 = global.attachEvent && !global[addEventListener]; - -// Helper function determines which prefixes of CSS calc we need. -// We only need to do this once on startup, when this anonymous function is called. -// -// Tests -webkit, -moz and -o prefixes. Modified from StackOverflow: -// http://stackoverflow.com/questions/16625140/js-feature-detection-to-detect-the-usage-of-webkit-calc-over-calc/16625167#16625167 -var calc = (['', '-webkit-', '-moz-', '-o-'].filter(function (prefix) { - var el = document.createElement('div'); - el.style.cssText = "width:" + prefix + "calc(9px)"; - - return (!!el.style.length) -}).shift()) + "calc"; - -// Helper function checks if its argument is a string-like type -var isString = function (v) { return (typeof v === 'string' || v instanceof String); }; - -// Helper function allows elements and string selectors to be used -// interchangeably. In either case an element is returned. This allows us to -// do `Split([elem1, elem2])` as well as `Split(['#id1', '#id2'])`. -var elementOrSelector = function (el) { - if (isString(el)) { - var ele = document.querySelector(el); - if (!ele) { - throw new Error(("Selector " + el + " did match a DOM element")) + // The programming goals of Split.js are to deliver readable, understandable and + // maintainable code, while at the same time manually optimizing for tiny minified file size, + // browser compatibility without additional requirements, graceful fallback (IE8 is supported) + // and very few assumptions about the user's page layout. + var global = window; + var document = global.document; + + // Save a couple long function names that are used frequently. + // This optimization saves around 400 bytes. + var addEventListener = 'addEventListener'; + var removeEventListener = 'removeEventListener'; + var getBoundingClientRect = 'getBoundingClientRect'; + var gutterStartDragging = '_a'; + var aGutterSize = '_b'; + var bGutterSize = '_c'; + var HORIZONTAL = 'horizontal'; + var NOOP = function () { return false; }; + + // Figure out if we're in IE8 or not. IE8 will still render correctly, + // but will be static instead of draggable. + var isIE8 = global.attachEvent && !global[addEventListener]; + + // Helper function determines which prefixes of CSS calc we need. + // We only need to do this once on startup, when this anonymous function is called. + // + // Tests -webkit, -moz and -o prefixes. Modified from StackOverflow: + // http://stackoverflow.com/questions/16625140/js-feature-detection-to-detect-the-usage-of-webkit-calc-over-calc/16625167#16625167 + var calc = (['', '-webkit-', '-moz-', '-o-'].filter(function (prefix) { + var el = document.createElement('div'); + el.style.cssText = "width:" + prefix + "calc(9px)"; + + return (!!el.style.length) + }).shift()) + "calc"; + + // Helper function checks if its argument is a string-like type + var isString = function (v) { return (typeof v === 'string' || v instanceof String); }; + + // Helper function allows elements and string selectors to be used + // interchangeably. In either case an element is returned. This allows us to + // do `Split([elem1, elem2])` as well as `Split(['#id1', '#id2'])`. + var elementOrSelector = function (el) { + if (isString(el)) { + var ele = document.querySelector(el); + if (!ele) { + throw new Error(("Selector " + el + " did match a DOM element")) + } + return ele + } + + return el + }; + + // Helper function gets a property from the properties object, with a default fallback + var getOption = function (options, propName, def) { + var value = options[propName]; + if (value !== undefined) { + return value } - return ele - } - - return el -}; - -// Helper function gets a property from the properties object, with a default fallback -var getOption = function (options, propName, def) { - var value = options[propName]; - if (value !== undefined) { - return value - } - return def -}; - -// Default options -var defaultGutterFn = function (i, gutterDirection) { - var gut = document.createElement('div'); - gut.className = "gutter gutter-" + gutterDirection; - return gut -}; - -var defaultElementStyleFn = function (dim, size, gutSize) { - var style = {}; - - if (!isString(size)) { - if (!isIE8) { - style[dim] = calc + "(" + size + "% - " + gutSize + "px)"; + return def + }; + + // Default options + var defaultGutterFn = function (i, gutterDirection) { + var gut = document.createElement('div'); + gut.className = "gutter gutter-" + gutterDirection; + return gut + }; + + var defaultElementStyleFn = function (dim, size, gutSize) { + var style = {}; + + if (!isString(size)) { + if (!isIE8) { + style[dim] = calc + "(" + size + "% - " + gutSize + "px)"; + } else { + style[dim] = size + "%"; + } } else { - style[dim] = size + "%"; + style[dim] = size; } - } else { - style[dim] = size; - } - - return style -}; - -var defaultGutterStyleFn = function (dim, gutSize) { return (( obj = {}, obj[dim] = (gutSize + "px"), obj )) - var obj; }; - -// The main function to initialize a split. Split.js thinks about each pair -// of elements as an independant pair. Dragging the gutter between two elements -// only changes the dimensions of elements in that pair. This is key to understanding -// how the following functions operate, since each function is bound to a pair. -// -// A pair object is shaped like this: -// -// { -// a: DOM element, -// b: DOM element, -// aMin: Number, -// bMin: Number, -// dragging: Boolean, -// parent: DOM element, -// isFirst: Boolean, -// isLast: Boolean, -// direction: 'horizontal' | 'vertical' -// } -// -// The basic sequence: -// -// 1. Set defaults to something sane. `options` doesn't have to be passed at all. -// 2. Initialize a bunch of strings based on the direction we're splitting. -// A lot of the behavior in the rest of the library is paramatized down to -// rely on CSS strings and classes. -// 3. Define the dragging helper functions, and a few helpers to go with them. -// 4. Loop through the elements while pairing them off. Every pair gets an -// `pair` object, a gutter, and special isFirst/isLast properties. -// 5. Actually size the pair elements, insert gutters and attach event listeners. -var Split = function (idsOption, options) { - if ( options === void 0 ) options = {}; - - var ids = idsOption; - var dimension; - var clientAxis; - var position; - var elements; - - // Allow HTMLCollection to be used as an argument when supported - if (Array.from) { - ids = Array.from(ids); - } - - // All DOM elements in the split should have a common parent. We can grab - // the first elements parent and hope users read the docs because the - // behavior will be whacky otherwise. - var firstElement = elementOrSelector(ids[0]); - var parent = firstElement.parentNode; - var parentFlexDirection = global.getComputedStyle(parent).flexDirection; - - // Set default options.sizes to equal percentages of the parent element. - var sizes = getOption(options, 'sizes') || ids.map(function () { return 100 / ids.length; }); - - // Standardize minSize to an array if it isn't already. This allows minSize - // to be passed as a number. - var minSize = getOption(options, 'minSize', 100); - var minSizes = Array.isArray(minSize) ? minSize : ids.map(function () { return minSize; }); - - // Get other options - var expandToMin = getOption(options, 'expandToMin', false); - var gutterSize = getOption(options, 'gutterSize', 10); - var snapOffset = getOption(options, 'snapOffset', 30); - var direction = getOption(options, 'direction', HORIZONTAL); - var cursor = getOption(options, 'cursor', direction === HORIZONTAL ? 'ew-resize' : 'ns-resize'); - var gutter = getOption(options, 'gutter', defaultGutterFn); - var elementStyle = getOption(options, 'elementStyle', defaultElementStyleFn); - var gutterStyle = getOption(options, 'gutterStyle', defaultGutterStyleFn); - // 2. Initialize a bunch of strings based on the direction we're splitting. - // A lot of the behavior in the rest of the library is paramatized down to - // rely on CSS strings and classes. - if (direction === HORIZONTAL) { - dimension = 'width'; - clientAxis = 'clientX'; - position = 'left'; - } else if (direction === 'vertical') { - dimension = 'height'; - clientAxis = 'clientY'; - position = 'top'; - } + return style + }; - // 3. Define the dragging helper functions, and a few helpers to go with them. - // Each helper is bound to a pair object that contains its metadata. This - // also makes it easy to store references to listeners that that will be - // added and removed. - // - // Even though there are no other functions contained in them, aliasing - // this to self saves 50 bytes or so since it's used so frequently. - // - // The pair object saves metadata like dragging state, position and - // event listener references. - - function setElementSize (el, size, gutSize) { - // Split.js allows setting sizes via numbers (ideally), or if you must, - // by string, like '300px'. This is less than ideal, because it breaks - // the fluid layout that `calc(% - px)` provides. You're on your own if you do that, - // make sure you calculate the gutter size by hand. - var style = elementStyle(dimension, size, gutSize); - - // eslint-disable-next-line no-param-reassign - Object.keys(style).forEach(function (prop) { - el.style[prop] = style[prop]; - }); - } + var defaultGutterStyleFn = function (dim, gutSize) { + var obj; - function setGutterSize (gutterElement, gutSize) { - var style = gutterStyle(dimension, gutSize); + return (( obj = {}, obj[dim] = (gutSize + "px"), obj )); + }; - // eslint-disable-next-line no-param-reassign - Object.keys(style).forEach(function (prop) { - gutterElement.style[prop] = style[prop]; - }); - } - - function getSizes () { - return elements.map(function (element) { return element.size; }) - } - - // Actually adjust the size of elements `a` and `b` to `offset` while dragging. - // calc is used to allow calc(percentage + gutterpx) on the whole split instance, - // which allows the viewport to be resized without additional logic. - // Element a's size is the same as offset. b's size is total size - a size. - // Both sizes are calculated from the initial parent percentage, - // then the gutter size is subtracted. - function adjust (offset) { - var a = elements[this.a]; - var b = elements[this.b]; - var percentage = a.size + b.size; - - a.size = (offset / this.size) * percentage; - b.size = (percentage - ((offset / this.size) * percentage)); - - setElementSize(a.element, a.size, this[aGutterSize]); - setElementSize(b.element, b.size, this[bGutterSize]); - } - - // drag, where all the magic happens. The logic is really quite simple: + // The main function to initialize a split. Split.js thinks about each pair + // of elements as an independant pair. Dragging the gutter between two elements + // only changes the dimensions of elements in that pair. This is key to understanding + // how the following functions operate, since each function is bound to a pair. // - // 1. Ignore if the pair is not dragging. - // 2. Get the offset of the event. - // 3. Snap offset to min if within snappable range (within min + snapOffset). - // 4. Actually adjust each element in the pair to offset. + // A pair object is shaped like this: // - // --------------------------------------------------------------------- - // | | <- a.minSize || b.minSize -> | | - // | | | <- this.snapOffset || this.snapOffset -> | | | - // | | | || | | | - // | | | || | | | - // --------------------------------------------------------------------- - // | <- this.start this.size -> | - function drag (e) { - var offset; - var a = elements[this.a]; - var b = elements[this.b]; - - if (!this.dragging) { return } - - // Get the offset of the event from the first side of the - // pair `this.start`. Supports touch events, but not multitouch, so only the first - // finger `touches[0]` is counted. - if ('touches' in e) { - offset = e.touches[0][clientAxis] - this.start; - } else { - offset = e[clientAxis] - this.start; + // { + // a: DOM element, + // b: DOM element, + // aMin: Number, + // bMin: Number, + // dragging: Boolean, + // parent: DOM element, + // isFirst: Boolean, + // isLast: Boolean, + // direction: 'horizontal' | 'vertical' + // } + // + // The basic sequence: + // + // 1. Set defaults to something sane. `options` doesn't have to be passed at all. + // 2. Initialize a bunch of strings based on the direction we're splitting. + // A lot of the behavior in the rest of the library is paramatized down to + // rely on CSS strings and classes. + // 3. Define the dragging helper functions, and a few helpers to go with them. + // 4. Loop through the elements while pairing them off. Every pair gets an + // `pair` object, a gutter, and special isFirst/isLast properties. + // 5. Actually size the pair elements, insert gutters and attach event listeners. + var Split = function (idsOption, options) { + if ( options === void 0 ) options = {}; + + var ids = idsOption; + var dimension; + var clientAxis; + var position; + var elements; + + // Allow HTMLCollection to be used as an argument when supported + if (Array.from) { + ids = Array.from(ids); } - // If within snapOffset of min or max, set offset to min or max. - // snapOffset buffers a.minSize and b.minSize, so logic is opposite for both. - // Include the appropriate gutter sizes to prevent overflows. - if (offset <= a.minSize + snapOffset + this[aGutterSize]) { - offset = a.minSize + this[aGutterSize]; - } else if (offset >= this.size - (b.minSize + snapOffset + this[bGutterSize])) { - offset = this.size - (b.minSize + this[bGutterSize]); + // All DOM elements in the split should have a common parent. We can grab + // the first elements parent and hope users read the docs because the + // behavior will be whacky otherwise. + var firstElement = elementOrSelector(ids[0]); + var parent = firstElement.parentNode; + var parentFlexDirection = global.getComputedStyle(parent).flexDirection; + + // Set default options.sizes to equal percentages of the parent element. + var sizes = getOption(options, 'sizes') || ids.map(function () { return 100 / ids.length; }); + + // Standardize minSize to an array if it isn't already. This allows minSize + // to be passed as a number. + var minSize = getOption(options, 'minSize', 100); + var minSizes = Array.isArray(minSize) ? minSize : ids.map(function () { return minSize; }); + + // Get other options + var expandToMin = getOption(options, 'expandToMin', false); + var gutterSize = getOption(options, 'gutterSize', 10); + var snapOffset = getOption(options, 'snapOffset', 30); + var direction = getOption(options, 'direction', HORIZONTAL); + var cursor = getOption(options, 'cursor', direction === HORIZONTAL ? 'ew-resize' : 'ns-resize'); + var gutter = getOption(options, 'gutter', defaultGutterFn); + var elementStyle = getOption(options, 'elementStyle', defaultElementStyleFn); + var gutterStyle = getOption(options, 'gutterStyle', defaultGutterStyleFn); + + // 2. Initialize a bunch of strings based on the direction we're splitting. + // A lot of the behavior in the rest of the library is paramatized down to + // rely on CSS strings and classes. + if (direction === HORIZONTAL) { + dimension = 'width'; + clientAxis = 'clientX'; + position = 'left'; + } else if (direction === 'vertical') { + dimension = 'height'; + clientAxis = 'clientY'; + position = 'top'; } - // Actually adjust the size. - adjust.call(this, offset); + // 3. Define the dragging helper functions, and a few helpers to go with them. + // Each helper is bound to a pair object that contains its metadata. This + // also makes it easy to store references to listeners that that will be + // added and removed. + // + // Even though there are no other functions contained in them, aliasing + // this to self saves 50 bytes or so since it's used so frequently. + // + // The pair object saves metadata like dragging state, position and + // event listener references. + + function setElementSize (el, size, gutSize) { + // Split.js allows setting sizes via numbers (ideally), or if you must, + // by string, like '300px'. This is less than ideal, because it breaks + // the fluid layout that `calc(% - px)` provides. You're on your own if you do that, + // make sure you calculate the gutter size by hand. + var style = elementStyle(dimension, size, gutSize); + + // eslint-disable-next-line no-param-reassign + Object.keys(style).forEach(function (prop) { + el.style[prop] = style[prop]; + }); + } - // Call the drag callback continously. Don't do anything too intensive - // in this callback. - getOption(options, 'onDrag', NOOP)(); - } + function setGutterSize (gutterElement, gutSize) { + var style = gutterStyle(dimension, gutSize); - // Cache some important sizes when drag starts, so we don't have to do that - // continously: - // - // `size`: The total size of the pair. First + second + first gutter + second gutter. - // `start`: The leading side of the first element. - // - // ------------------------------------------------ - // | aGutterSize -> ||| | - // | ||| | - // | ||| | - // | ||| <- bGutterSize | - // ------------------------------------------------ - // | <- start size -> | - function calculateSizes () { - // Figure out the parent size minus padding. - var a = elements[this.a].element; - var b = elements[this.b].element; - - var aBounds = a[getBoundingClientRect](); - var bBounds = b[getBoundingClientRect](); - - this.size = aBounds[dimension] + bBounds[dimension] + this[aGutterSize] + this[bGutterSize]; - this.start = aBounds[position]; - } - - // stopDragging is very similar to startDragging in reverse. - function stopDragging () { - var self = this; - var a = elements[self.a].element; - var b = elements[self.b].element; - - if (self.dragging) { - getOption(options, 'onDragEnd', NOOP)(getSizes()); + // eslint-disable-next-line no-param-reassign + Object.keys(style).forEach(function (prop) { + gutterElement.style[prop] = style[prop]; + }); } - self.dragging = false; - - // Remove the stored event listeners. This is why we store them. - global[removeEventListener]('mouseup', self.stop); - global[removeEventListener]('touchend', self.stop); - global[removeEventListener]('touchcancel', self.stop); - global[removeEventListener]('mousemove', self.move); - global[removeEventListener]('touchmove', self.move); - - // Clear bound function references - self.stop = null; - self.move = null; - - a[removeEventListener]('selectstart', NOOP); - a[removeEventListener]('dragstart', NOOP); - b[removeEventListener]('selectstart', NOOP); - b[removeEventListener]('dragstart', NOOP); - - a.style.userSelect = ''; - a.style.webkitUserSelect = ''; - a.style.MozUserSelect = ''; - a.style.pointerEvents = ''; - - b.style.userSelect = ''; - b.style.webkitUserSelect = ''; - b.style.MozUserSelect = ''; - b.style.pointerEvents = ''; - - self.gutter.style.cursor = ''; - self.parent.style.cursor = ''; - document.body.style.cursor = ''; - } - - // startDragging calls `calculateSizes` to store the inital size in the pair object. - // It also adds event listeners for mouse/touch events, - // and prevents selection while dragging so avoid the selecting text. - function startDragging (e) { - // Alias frequently used variables to save space. 200 bytes. - var self = this; - var a = elements[self.a].element; - var b = elements[self.b].element; - - // Call the onDragStart callback. - if (!self.dragging) { - getOption(options, 'onDragStart', NOOP)(getSizes()); + function getSizes () { + return elements.map(function (element) { return element.size; }) } - // Don't actually drag the element. We emulate that in the drag function. - e.preventDefault(); - - // Set the dragging property of the pair object. - self.dragging = true; - - // Create two event listeners bound to the same pair object and store - // them in the pair object. - self.move = drag.bind(self); - self.stop = stopDragging.bind(self); - - // All the binding. `window` gets the stop events in case we drag out of the elements. - global[addEventListener]('mouseup', self.stop); - global[addEventListener]('touchend', self.stop); - global[addEventListener]('touchcancel', self.stop); - global[addEventListener]('mousemove', self.move); - global[addEventListener]('touchmove', self.move); - - // Disable selection. Disable! - a[addEventListener]('selectstart', NOOP); - a[addEventListener]('dragstart', NOOP); - b[addEventListener]('selectstart', NOOP); - b[addEventListener]('dragstart', NOOP); - - a.style.userSelect = 'none'; - a.style.webkitUserSelect = 'none'; - a.style.MozUserSelect = 'none'; - a.style.pointerEvents = 'none'; - - b.style.userSelect = 'none'; - b.style.webkitUserSelect = 'none'; - b.style.MozUserSelect = 'none'; - b.style.pointerEvents = 'none'; - - // Set the cursor at multiple levels - self.gutter.style.cursor = cursor; - self.parent.style.cursor = cursor; - document.body.style.cursor = cursor; - - // Cache the initial sizes of the pair. - calculateSizes.call(self); - } - - // 5. Create pair and element objects. Each pair has an index reference to - // elements `a` and `b` of the pair (first and second elements). - // Loop through the elements while pairing them off. Every pair gets a - // `pair` object, a gutter, and isFirst/isLast properties. - // - // Basic logic: - // - // - Starting with the second element `i > 0`, create `pair` objects with - // `a = i - 1` and `b = i` - // - Set gutter sizes based on the _pair_ being first/last. The first and last - // pair have gutterSize / 2, since they only have one half gutter, and not two. - // - Create gutter elements and add event listeners. - // - Set the size of the elements, minus the gutter sizes. - // - // ----------------------------------------------------------------------- - // | i=0 | i=1 | i=2 | i=3 | - // | | isFirst | | isLast | - // | pair 0 pair 1 pair 2 | - // | | | | | - // ----------------------------------------------------------------------- - var pairs = []; - elements = ids.map(function (id, i) { - // Create the element object. - var element = { - element: elementOrSelector(id), - size: sizes[i], - minSize: minSizes[i], - i: i, - }; - - var pair; - - if (i > 0) { - // Create the pair object with its metadata. - pair = { - a: i - 1, - b: i, - dragging: false, - isFirst: (i === 1), - isLast: (i === ids.length - 1), - direction: direction, - parent: parent, - }; - - // For first and last pairs, first and last gutter width is half. - pair[aGutterSize] = gutterSize; - pair[bGutterSize] = gutterSize; + // Actually adjust the size of elements `a` and `b` to `offset` while dragging. + // calc is used to allow calc(percentage + gutterpx) on the whole split instance, + // which allows the viewport to be resized without additional logic. + // Element a's size is the same as offset. b's size is total size - a size. + // Both sizes are calculated from the initial parent percentage, + // then the gutter size is subtracted. + function adjust (offset) { + var a = elements[this.a]; + var b = elements[this.b]; + var percentage = a.size + b.size; + + a.size = (offset / this.size) * percentage; + b.size = (percentage - ((offset / this.size) * percentage)); + + setElementSize(a.element, a.size, this[aGutterSize]); + setElementSize(b.element, b.size, this[bGutterSize]); + } - if (pair.isFirst) { - pair[aGutterSize] = gutterSize / 2; + // drag, where all the magic happens. The logic is really quite simple: + // + // 1. Ignore if the pair is not dragging. + // 2. Get the offset of the event. + // 3. Snap offset to min if within snappable range (within min + snapOffset). + // 4. Actually adjust each element in the pair to offset. + // + // --------------------------------------------------------------------- + // | | <- a.minSize || b.minSize -> | | + // | | | <- this.snapOffset || this.snapOffset -> | | | + // | | | || | | | + // | | | || | | | + // --------------------------------------------------------------------- + // | <- this.start this.size -> | + function drag (e) { + var offset; + var a = elements[this.a]; + var b = elements[this.b]; + + if (!this.dragging) { return } + + // Get the offset of the event from the first side of the + // pair `this.start`. Supports touch events, but not multitouch, so only the first + // finger `touches[0]` is counted. + if ('touches' in e) { + offset = e.touches[0][clientAxis] - this.start; + } else { + offset = e[clientAxis] - this.start; } - if (pair.isLast) { - pair[bGutterSize] = gutterSize / 2; + // If within snapOffset of min or max, set offset to min or max. + // snapOffset buffers a.minSize and b.minSize, so logic is opposite for both. + // Include the appropriate gutter sizes to prevent overflows. + if (offset <= a.minSize + snapOffset + this[aGutterSize]) { + offset = a.minSize + this[aGutterSize]; + } else if (offset >= this.size - (b.minSize + snapOffset + this[bGutterSize])) { + offset = this.size - (b.minSize + this[bGutterSize]); } - // if the parent has a reverse flex-direction, switch the pair elements. - if (parentFlexDirection === 'row-reverse' || parentFlexDirection === 'column-reverse') { - var temp = pair.a; - pair.a = pair.b; - pair.b = temp; - } + // Actually adjust the size. + adjust.call(this, offset); + + // Call the drag callback continously. Don't do anything too intensive + // in this callback. + getOption(options, 'onDrag', NOOP)(); } - // Determine the size of the current element. IE8 is supported by - // staticly assigning sizes without draggable gutters. Assigns a string - // to `size`. + // Cache some important sizes when drag starts, so we don't have to do that + // continously: // - // IE9 and above - if (!isIE8) { - // Create gutter elements for each pair. - if (i > 0) { - var gutterElement = gutter(i, direction, element.element); - setGutterSize(gutterElement, gutterSize); - - // Save bound event listener for removal later - pair[gutterStartDragging] = startDragging.bind(pair); - - // Attach bound event listener - gutterElement[addEventListener]('mousedown', pair[gutterStartDragging]); - gutterElement[addEventListener]('touchstart', pair[gutterStartDragging]); + // `size`: The total size of the pair. First + second + first gutter + second gutter. + // `start`: The leading side of the first element. + // + // ------------------------------------------------ + // | aGutterSize -> ||| | + // | ||| | + // | ||| | + // | ||| <- bGutterSize | + // ------------------------------------------------ + // | <- start size -> | + function calculateSizes () { + // Figure out the parent size minus padding. + var a = elements[this.a].element; + var b = elements[this.b].element; + + var aBounds = a[getBoundingClientRect](); + var bBounds = b[getBoundingClientRect](); + + this.size = aBounds[dimension] + bBounds[dimension] + this[aGutterSize] + this[bGutterSize]; + this.start = aBounds[position]; + } - parent.insertBefore(gutterElement, element.element); + // stopDragging is very similar to startDragging in reverse. + function stopDragging () { + var self = this; + var a = elements[self.a].element; + var b = elements[self.b].element; - pair.gutter = gutterElement; + if (self.dragging) { + getOption(options, 'onDragEnd', NOOP)(getSizes()); } + + self.dragging = false; + + // Remove the stored event listeners. This is why we store them. + global[removeEventListener]('mouseup', self.stop); + global[removeEventListener]('touchend', self.stop); + global[removeEventListener]('touchcancel', self.stop); + global[removeEventListener]('mousemove', self.move); + global[removeEventListener]('touchmove', self.move); + + // Clear bound function references + self.stop = null; + self.move = null; + + a[removeEventListener]('selectstart', NOOP); + a[removeEventListener]('dragstart', NOOP); + b[removeEventListener]('selectstart', NOOP); + b[removeEventListener]('dragstart', NOOP); + + a.style.userSelect = ''; + a.style.webkitUserSelect = ''; + a.style.MozUserSelect = ''; + a.style.pointerEvents = ''; + + b.style.userSelect = ''; + b.style.webkitUserSelect = ''; + b.style.MozUserSelect = ''; + b.style.pointerEvents = ''; + + self.gutter.style.cursor = ''; + self.parent.style.cursor = ''; + document.body.style.cursor = ''; } - // Set the element size to our determined size. - // Half-size gutters for first and last elements. - var elementGutterSize = (i === 0 || i === ids.length - 1) ? gutterSize / 2 : gutterSize; - setElementSize(element.element, element.size, elementGutterSize); + // startDragging calls `calculateSizes` to store the inital size in the pair object. + // It also adds event listeners for mouse/touch events, + // and prevents selection while dragging so avoid the selecting text. + function startDragging (e) { + // Alias frequently used variables to save space. 200 bytes. + var self = this; + var a = elements[self.a].element; + var b = elements[self.b].element; + + // Call the onDragStart callback. + if (!self.dragging) { + getOption(options, 'onDragStart', NOOP)(getSizes()); + } - // After the first iteration, and we have a pair object, append it to the - // list of pairs. - if (i > 0) { - pairs.push(pair); + // Don't actually drag the element. We emulate that in the drag function. + e.preventDefault(); + + // Set the dragging property of the pair object. + self.dragging = true; + + // Create two event listeners bound to the same pair object and store + // them in the pair object. + self.move = drag.bind(self); + self.stop = stopDragging.bind(self); + + // All the binding. `window` gets the stop events in case we drag out of the elements. + global[addEventListener]('mouseup', self.stop); + global[addEventListener]('touchend', self.stop); + global[addEventListener]('touchcancel', self.stop); + global[addEventListener]('mousemove', self.move); + global[addEventListener]('touchmove', self.move); + + // Disable selection. Disable! + a[addEventListener]('selectstart', NOOP); + a[addEventListener]('dragstart', NOOP); + b[addEventListener]('selectstart', NOOP); + b[addEventListener]('dragstart', NOOP); + + a.style.userSelect = 'none'; + a.style.webkitUserSelect = 'none'; + a.style.MozUserSelect = 'none'; + a.style.pointerEvents = 'none'; + + b.style.userSelect = 'none'; + b.style.webkitUserSelect = 'none'; + b.style.MozUserSelect = 'none'; + b.style.pointerEvents = 'none'; + + // Set the cursor at multiple levels + self.gutter.style.cursor = cursor; + self.parent.style.cursor = cursor; + document.body.style.cursor = cursor; + + // Cache the initial sizes of the pair. + calculateSizes.call(self); } - return element - }); + // 5. Create pair and element objects. Each pair has an index reference to + // elements `a` and `b` of the pair (first and second elements). + // Loop through the elements while pairing them off. Every pair gets a + // `pair` object, a gutter, and isFirst/isLast properties. + // + // Basic logic: + // + // - Starting with the second element `i > 0`, create `pair` objects with + // `a = i - 1` and `b = i` + // - Set gutter sizes based on the _pair_ being first/last. The first and last + // pair have gutterSize / 2, since they only have one half gutter, and not two. + // - Create gutter elements and add event listeners. + // - Set the size of the elements, minus the gutter sizes. + // + // ----------------------------------------------------------------------- + // | i=0 | i=1 | i=2 | i=3 | + // | | isFirst | | isLast | + // | pair 0 pair 1 pair 2 | + // | | | | | + // ----------------------------------------------------------------------- + var pairs = []; + elements = ids.map(function (id, i) { + // Create the element object. + var element = { + element: elementOrSelector(id), + size: sizes[i], + minSize: minSizes[i], + i: i, + }; - function adjustToMin (element) { - var isLast = (element.i === pairs.length); - var pair = isLast ? pairs[element.i - 1] : pairs[element.i]; + var pair; + + if (i > 0) { + // Create the pair object with its metadata. + pair = { + a: i - 1, + b: i, + dragging: false, + isFirst: (i === 1), + isLast: (i === ids.length - 1), + direction: direction, + parent: parent, + }; + + // For first and last pairs, first and last gutter width is half. + pair[aGutterSize] = gutterSize; + pair[bGutterSize] = gutterSize; + + if (pair.isFirst) { + pair[aGutterSize] = gutterSize / 2; + } + + if (pair.isLast) { + pair[bGutterSize] = gutterSize / 2; + } + + // if the parent has a reverse flex-direction, switch the pair elements. + if (parentFlexDirection === 'row-reverse' || parentFlexDirection === 'column-reverse') { + var temp = pair.a; + pair.a = pair.b; + pair.b = temp; + } + } - calculateSizes.call(pair); + // Determine the size of the current element. IE8 is supported by + // staticly assigning sizes without draggable gutters. Assigns a string + // to `size`. + // + // IE9 and above + if (!isIE8) { + // Create gutter elements for each pair. + if (i > 0) { + var gutterElement = gutter(i, direction, element.element); + setGutterSize(gutterElement, gutterSize); - var size = isLast ? (pair.size - element.minSize - pair[bGutterSize]) : element.minSize + pair[aGutterSize]; + // Save bound event listener for removal later + pair[gutterStartDragging] = startDragging.bind(pair); - adjust.call(pair, size); - } + // Attach bound event listener + gutterElement[addEventListener]('mousedown', pair[gutterStartDragging]); + gutterElement[addEventListener]('touchstart', pair[gutterStartDragging]); - elements.forEach(function (element) { - var computedSize = element.element[getBoundingClientRect]()[dimension]; + parent.insertBefore(gutterElement, element.element); - if (computedSize < element.minSize) { - if (expandToMin) { - adjustToMin(element); - } else { - element.minSize = computedSize; + pair.gutter = gutterElement; + } } - } - }); - function setSizes (newSizes) { - newSizes.forEach(function (newSize, i) { + // Set the element size to our determined size. + // Half-size gutters for first and last elements. + var elementGutterSize = (i === 0 || i === ids.length - 1) ? gutterSize / 2 : gutterSize; + setElementSize(element.element, element.size, elementGutterSize); + + // After the first iteration, and we have a pair object, append it to the + // list of pairs. if (i > 0) { - var pair = pairs[i - 1]; - var a = elements[pair.a]; - var b = elements[pair.b]; + pairs.push(pair); + } + + return element + }); + + function adjustToMin (element) { + var isLast = (element.i === pairs.length); + var pair = isLast ? pairs[element.i - 1] : pairs[element.i]; + + calculateSizes.call(pair); - a.size = newSizes[i - 1]; - b.size = newSize; + var size = isLast ? (pair.size - element.minSize - pair[bGutterSize]) : element.minSize + pair[aGutterSize]; - setElementSize(a.element, a.size, pair[aGutterSize]); - setElementSize(b.element, b.size, pair[bGutterSize]); + adjust.call(pair, size); + } + + elements.forEach(function (element) { + var computedSize = element.element[getBoundingClientRect]()[dimension]; + + if (computedSize < element.minSize) { + if (expandToMin) { + adjustToMin(element); + } else { + element.minSize = computedSize; + } } }); - } - function destroy (preserveStyles, preserveGutter) { - pairs.forEach(function (pair) { - if (preserveGutter !== true) { - pair.parent.removeChild(pair.gutter); - } else { - pair.gutter[removeEventListener]('mousedown', pair[gutterStartDragging]); - pair.gutter[removeEventListener]('touchstart', pair[gutterStartDragging]); - } + function setSizes (newSizes) { + newSizes.forEach(function (newSize, i) { + if (i > 0) { + var pair = pairs[i - 1]; + var a = elements[pair.a]; + var b = elements[pair.b]; + + a.size = newSizes[i - 1]; + b.size = newSize; + + setElementSize(a.element, a.size, pair[aGutterSize]); + setElementSize(b.element, b.size, pair[bGutterSize]); + } + }); + } - if (preserveStyles !== true) { - var style = elementStyle(dimension, pair.a.size, pair[aGutterSize]); + function destroy (preserveStyles, preserveGutter) { + pairs.forEach(function (pair) { + if (preserveGutter !== true) { + pair.parent.removeChild(pair.gutter); + } else { + pair.gutter[removeEventListener]('mousedown', pair[gutterStartDragging]); + pair.gutter[removeEventListener]('touchstart', pair[gutterStartDragging]); + } + + if (preserveStyles !== true) { + var style = elementStyle(dimension, pair.a.size, pair[aGutterSize]); + + Object.keys(style).forEach(function (prop) { + elements[pair.a].element.style[prop] = ''; + elements[pair.b].element.style[prop] = ''; + }); + } + }); + } - Object.keys(style).forEach(function (prop) { - elements[pair.a].element.style[prop] = ''; - elements[pair.b].element.style[prop] = ''; - }); + if (isIE8) { + return { + setSizes: setSizes, + destroy: destroy, } - }); - } + } - if (isIE8) { return { setSizes: setSizes, + getSizes: getSizes, + collapse: function collapse (i) { + adjustToMin(elements[i]); + }, destroy: destroy, + parent: parent, + pairs: pairs, } - } - - return { - setSizes: setSizes, - getSizes: getSizes, - collapse: function collapse (i) { - adjustToMin(elements[i]); - }, - destroy: destroy, - parent: parent, - pairs: pairs, - } -}; - -return Split; + }; + + return Split; }))); diff --git a/split.min.js b/split.min.js index 7173f52b..fe11943b 100644 --- a/split.min.js +++ b/split.min.js @@ -1,2 +1,2 @@ /*! Split.js - v1.5.0 */ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Split=t()}(this,function(){"use strict";var M=window,O=M.document,j="addEventListener",A="removeEventListener",L="getBoundingClientRect",C="horizontal",B=function(){return!1},F=M.attachEvent&&!M[j],i=["","-webkit-","-moz-","-o-"].filter(function(e){var t=O.createElement("div");return t.style.cssText="width:"+e+"calc(9px)",!!t.style.length}).shift()+"calc",s=function(e){return"string"==typeof e||e instanceof String},N=function(e){if(s(e)){var t=O.querySelector(e);if(!t)throw new Error("Selector "+e+" did match a DOM element");return t}return e},T=function(e,t,n){var r=e[t];return void 0!==r?r:n},q=function(e,t){var n=O.createElement("div");return n.className="gutter gutter-"+t,n},R=function(e,t,n){var r={};return s(t)?r[e]=t:r[e]=F?t+"%":i+"("+t+"% - "+n+"px)",r},X=function(e,t){return(n={})[e]=t+"px",n;var n};return function(e,i){void 0===i&&(i={});var l,s,o,a,u=e;Array.from&&(u=Array.from(u));var f=N(u[0]).parentNode,m=M.getComputedStyle(f).flexDirection,h=T(i,"sizes")||u.map(function(){return 100/u.length}),t=T(i,"minSize",100),v=Array.isArray(t)?t:u.map(function(){return t}),n=T(i,"expandToMin",!1),d=T(i,"gutterSize",10),c=T(i,"snapOffset",30),g=T(i,"direction",C),y=T(i,"cursor",g===C?"ew-resize":"ns-resize"),z=T(i,"gutter",q),p=T(i,"elementStyle",R),S=T(i,"gutterStyle",X);function b(t,e,n){var r=p(l,e,n);Object.keys(r).forEach(function(e){t.style[e]=r[e]})}function _(){return a.map(function(e){return e.size})}function E(e){var t=a[this.a],n=a[this.b],r=t.size+n.size;t.size=e/this.size*r,n.size=r-e/this.size*r,b(t.element,t.size,this._b),b(n.element,n.size,this._c)}function w(){var e=a[this.a].element,t=a[this.b].element,n=e[L](),r=t[L]();this.size=n[l]+r[l]+this._b+this._c,this.start=n[o]}function k(e){var t=this,n=a[t.a].element,r=a[t.b].element;t.dragging||T(i,"onDragStart",B)(_()),e.preventDefault(),t.dragging=!0,t.move=function(e){var t,n=a[this.a],r=a[this.b];this.dragging&&((t="touches"in e?e.touches[0][s]-this.start:e[s]-this.start)<=n.minSize+c+this._b?t=n.minSize+this._b:t>=this.size-(r.minSize+c+this._c)&&(t=this.size-(r.minSize+this._c)),E.call(this,t),T(i,"onDrag",B)())}.bind(t),t.stop=function(){var e=this,t=a[e.a].element,n=a[e.b].element;e.dragging&&T(i,"onDragEnd",B)(_()),e.dragging=!1,M[A]("mouseup",e.stop),M[A]("touchend",e.stop),M[A]("touchcancel",e.stop),M[A]("mousemove",e.move),M[A]("touchmove",e.move),e.stop=null,e.move=null,t[A]("selectstart",B),t[A]("dragstart",B),n[A]("selectstart",B),n[A]("dragstart",B),t.style.userSelect="",t.style.webkitUserSelect="",t.style.MozUserSelect="",t.style.pointerEvents="",n.style.userSelect="",n.style.webkitUserSelect="",n.style.MozUserSelect="",n.style.pointerEvents="",e.gutter.style.cursor="",e.parent.style.cursor="",O.body.style.cursor=""}.bind(t),M[j]("mouseup",t.stop),M[j]("touchend",t.stop),M[j]("touchcancel",t.stop),M[j]("mousemove",t.move),M[j]("touchmove",t.move),n[j]("selectstart",B),n[j]("dragstart",B),r[j]("selectstart",B),r[j]("dragstart",B),n.style.userSelect="none",n.style.webkitUserSelect="none",n.style.MozUserSelect="none",n.style.pointerEvents="none",r.style.userSelect="none",r.style.webkitUserSelect="none",r.style.MozUserSelect="none",r.style.pointerEvents="none",t.gutter.style.cursor=y,t.parent.style.cursor=y,O.body.style.cursor=y,w.call(t)}g===C?(l="width",s="clientX",o="left"):"vertical"===g&&(l="height",s="clientY",o="top");var x=[];function r(e){var t=e.i===x.length,n=t?x[e.i-1]:x[e.i];w.call(n);var r=t?n.size-e.minSize-n._c:e.minSize+n._b;E.call(n,r)}function U(s){s.forEach(function(e,t){if(0=this.size-(r.minSize+c+this._c)&&(t=this.size-(r.minSize+this._c)),E.call(this,t),T(i,"onDrag",B)())}.bind(t),t.stop=function(){var e=this,t=a[e.a].element,n=a[e.b].element;e.dragging&&T(i,"onDragEnd",B)(_()),e.dragging=!1,M[A]("mouseup",e.stop),M[A]("touchend",e.stop),M[A]("touchcancel",e.stop),M[A]("mousemove",e.move),M[A]("touchmove",e.move),e.stop=null,e.move=null,t[A]("selectstart",B),t[A]("dragstart",B),n[A]("selectstart",B),n[A]("dragstart",B),t.style.userSelect="",t.style.webkitUserSelect="",t.style.MozUserSelect="",t.style.pointerEvents="",n.style.userSelect="",n.style.webkitUserSelect="",n.style.MozUserSelect="",n.style.pointerEvents="",e.gutter.style.cursor="",e.parent.style.cursor="",O.body.style.cursor=""}.bind(t),M[j]("mouseup",t.stop),M[j]("touchend",t.stop),M[j]("touchcancel",t.stop),M[j]("mousemove",t.move),M[j]("touchmove",t.move),n[j]("selectstart",B),n[j]("dragstart",B),r[j]("selectstart",B),r[j]("dragstart",B),n.style.userSelect="none",n.style.webkitUserSelect="none",n.style.MozUserSelect="none",n.style.pointerEvents="none",r.style.userSelect="none",r.style.webkitUserSelect="none",r.style.MozUserSelect="none",r.style.pointerEvents="none",t.gutter.style.cursor=y,t.parent.style.cursor=y,O.body.style.cursor=y,w.call(t)}g===C?(l="width",s="clientX",o="left"):"vertical"===g&&(l="height",s="clientY",o="top");var x=[];function r(e){var t=e.i===x.length,n=t?x[e.i-1]:x[e.i];w.call(n);var r=t?n.size-e.minSize-n._c:e.minSize+n._b;E.call(n,r)}function U(s){s.forEach(function(e,t){if(0