Showing preview only (1,255K chars total). Download the full file or copy to clipboard to get everything.
Repository: haltu/muuri
Branch: master
Commit: 219e00893327
Files: 175
Total size: 1.2 MB
Directory structure:
gitextract_btx3_qaq/
├── .eslintrc.js
├── .github/
│ └── FUNDING.yml
├── .gitignore
├── .prettierrc.json
├── .travis.yml
├── AUTHORS.txt
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── dist/
│ ├── muuri.js
│ └── muuri.module.js
├── docs/
│ ├── .vitepress/
│ │ ├── config.js
│ │ └── theme/
│ │ ├── custom.css
│ │ └── index.js
│ ├── examples.md
│ ├── getting-started.md
│ ├── grid-constructor.md
│ ├── grid-events.md
│ ├── grid-methods.md
│ ├── grid-options.md
│ ├── index.md
│ └── item-methods.md
├── gulpfile.js
├── karma.conf.js
├── karma.defaults.js
├── package.json
├── rollup.banner.js
├── rollup.config.js
├── src/
│ ├── Animator/
│ │ └── Animator.js
│ ├── AutoScroller/
│ │ ├── AutoScroller.js
│ │ ├── LICENSE.md
│ │ ├── Pool.js
│ │ ├── ScrollAction.js
│ │ ├── ScrollRequest.js
│ │ ├── constants.js
│ │ └── utils.js
│ ├── Dragger/
│ │ ├── Dragger.js
│ │ ├── EdgeHack.js
│ │ └── LICENSE.md
│ ├── Emitter/
│ │ ├── Emitter.js
│ │ └── LICENSE.md
│ ├── Grid/
│ │ └── Grid.js
│ ├── Item/
│ │ ├── Item.js
│ │ ├── ItemDrag.js
│ │ ├── ItemDragPlaceholder.js
│ │ ├── ItemDragRelease.js
│ │ ├── ItemLayout.js
│ │ ├── ItemMigrate.js
│ │ └── ItemVisibility.js
│ ├── Packer/
│ │ ├── LICENSE.md
│ │ ├── Packer.js
│ │ └── PackerProcessor.js
│ ├── Ticker/
│ │ ├── LICENSE.md
│ │ └── Ticker.js
│ ├── constants.js
│ ├── index.d.ts
│ ├── index.js
│ ├── ticker.js
│ └── utils/
│ ├── addClass.js
│ ├── arrayInsert.js
│ ├── arrayMove.js
│ ├── arraySwap.js
│ ├── createUid.js
│ ├── debounce.js
│ ├── elementMatches.js
│ ├── getContainingBlock.js
│ ├── getCurrentStyles.js
│ ├── getIntersectionArea.js
│ ├── getIntersectionScore.js
│ ├── getOffsetDiff.js
│ ├── getPrefixedPropName.js
│ ├── getScrollableAncestors.js
│ ├── getStyle.js
│ ├── getStyleAsFloat.js
│ ├── getStyleName.js
│ ├── getTranslate.js
│ ├── getTranslateString.js
│ ├── getUnprefixedPropName.js
│ ├── hasPassiveEvents.js
│ ├── isFunction.js
│ ├── isNative.js
│ ├── isNodeList.js
│ ├── isOverlapping.js
│ ├── isPlainObject.js
│ ├── isScrollable.js
│ ├── isTransformed.js
│ ├── noop.js
│ ├── normalizeArrayIndex.js
│ ├── raf.js
│ ├── removeClass.js
│ ├── setStyles.js
│ ├── toArray.js
│ ├── transformProp.js
│ └── transformStyle.js
└── tests/
├── grid-constructor/
│ ├── container.js
│ └── instance.js
├── grid-events/
│ ├── add.js
│ ├── beforeReceive.js
│ ├── beforeSend.js
│ ├── destroy.js
│ ├── dragEnd.js
│ ├── dragInit.js
│ ├── dragMove.js
│ ├── dragReleaseEnd.js
│ ├── dragReleaseStart.js
│ ├── dragScroll.js
│ ├── dragStart.js
│ ├── draggerEvent.js
│ ├── filter.js
│ ├── hideEnd.js
│ ├── hideStart.js
│ ├── layoutAbort.js
│ ├── layoutEnd.js
│ ├── layoutStart.js
│ ├── move.js
│ ├── receive.js
│ ├── remove.js
│ ├── send.js
│ ├── showEnd.js
│ ├── showStart.js
│ ├── sort.js
│ └── synchronize.js
├── grid-methods/
│ ├── add.js
│ ├── destroy.js
│ ├── filter.js
│ ├── getElement.js
│ ├── getItems.js
│ ├── hide.js
│ ├── layout.js
│ ├── move.js
│ ├── off.js
│ ├── on.js
│ ├── refreshItems.js
│ ├── refreshSortData.js
│ ├── remove.js
│ ├── send.js
│ ├── show.js
│ ├── sort.js
│ └── synchronize.js
├── grid-options/
│ ├── containerClass.js
│ ├── dragAutoScroll.js
│ ├── dragAxis.js
│ ├── dragContainer.js
│ ├── dragEnabled.js
│ ├── dragPlaceholder.js
│ ├── dragSort.js
│ ├── dragSortPredicate.js
│ ├── dragStartPredicate.js
│ ├── hideDuration.js
│ ├── itemClass.js
│ ├── itemDraggingClass.js
│ ├── itemHiddenClass.js
│ ├── itemPositioningClass.js
│ ├── itemReleasingClass.js
│ ├── itemVisibleClass.js
│ ├── items.js
│ ├── layout.js
│ ├── showDuration.js
│ └── visibleStyles-hiddenStyles.js
├── index.js
├── item-methods/
│ ├── getElement.js
│ ├── getGrid.js
│ ├── getHeight.js
│ ├── getMargin.js
│ ├── getPosition.js
│ ├── getWidth.js
│ ├── isActive.js
│ ├── isDestroyed.js
│ ├── isDragging.js
│ ├── isHiding.js
│ ├── isPositioning.js
│ ├── isReleasing.js
│ ├── isShowing.js
│ └── isVisible.js
└── utils.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .eslintrc.js
================================================
module.exports = {
parserOptions: {
ecmaVersion: 7,
sourceType: 'module'
},
rules: {
// Possible errors
'no-duplicate-case': 'error',
'no-ex-assign': 'error',
'no-empty': ['error', { allowEmptyCatch: true }],
'no-extra-semi': 'error',
'no-func-assign': 'error',
'no-invalid-regexp': 'error',
'no-irregular-whitespace': 'error',
'no-obj-calls': 'error',
'no-sparse-arrays': 'error',
'no-unexpected-multiline': 'error',
'no-unreachable': 'error',
'no-unsafe-finally': 'error',
'use-isnan': 'error',
'valid-typeof': 'error',
// Best practices
'array-callback-return': 'error',
eqeqeq: ['error', 'always'],
'no-caller': 'error',
'no-case-declarations': 'error',
'no-empty-pattern': 'error',
'no-eq-null': 'error',
'no-eval': 'error',
'no-extend-native': 'error',
'no-extra-bind': 'error',
'no-extra-label': 'error',
'no-fallthrough': 'error',
'no-floating-decimal': 'error',
'no-global-assign': 'error',
'no-implicit-globals': 'error',
'no-implied-eval': 'error',
'no-iterator': 'error',
'no-lone-blocks': 'error',
'no-multi-spaces': 'error',
'no-multi-str': 'error',
'no-octal-escape': 'error',
'no-octal': 'error',
'no-proto': 'error',
'no-redeclare': 'error',
'no-return-await': 'error',
'no-script-url': 'error',
'no-self-assign': 'error',
'no-self-compare': 'error',
'no-sequences': 'error',
'no-throw-literal': 'error',
'no-unmodified-loop-condition': 'error',
'no-unused-expressions': ['error', { allowShortCircuit: true, allowTernary: true }],
'no-unused-labels': 'error',
'no-useless-call': 'error',
'no-useless-concat': 'error',
'no-useless-escape': 'error',
'no-useless-return': 'error',
'no-with': 'error',
radix: ['error', 'as-needed'],
'no-delete-var': 'error',
'no-label-var': 'error',
'no-undef-init': 'error',
'no-unused-vars': 'error'
}
};
================================================
FILE: .github/FUNDING.yml
================================================
github: [niklasramo]
================================================
FILE: .gitignore
================================================
node_modules
website
/coverage
/website
*.log
*.env
*.DS_Store
docs/.vitepress/dist
================================================
FILE: .prettierrc.json
================================================
{
"printWidth": 100,
"singleQuote": true
}
================================================
FILE: .travis.yml
================================================
language: node_js
node_js:
- '12'
env:
email: false
global:
- secure: UcHda6DVxvFcQPscXNG/6jd0pwVJoik0wx7AldoJSr1v5hZGXhdVuPUYpLVK53uhifl6DNxsovzCJF41zAg7QhJSqNaQlDXhoLghAC4iL/6VsVJBu4Jx2KZKrsPeMVJxqA6Q5FLQzF91jpkgV+pWf2g2RnXkDnm81EWIVQDhg83bs4HhrRTzdsND2nkl8iSucO9Nm3tDjC8rNaa9jagGyF3ZVTO+B/L/E0opNxPzZFdxL6UrQYjrplHjF4HEoNyBBLWf4p2Tco49xDFBNoTs8B+pBmI1i6WufuWiBhhMTwCYaKwQ0HGjKldC/v3kgYF3k+pnRQtQZdjlFrP48JI+BLn2lg0KrgU37xyDRatpTpOVD+sZ5rJlVIBYfTxgMBfdr4vQxXdXfQDwydCPl13b76XavJxEiivXvPP12IR1WD7Lpa6Smd8jroROCexwQiE8HKLaXF9uuLikgzqq2rbUNzKb3xxhMoRNNs194V44ez6oq5a2S5i9JLVmYyoGJP/hjCM7lM82mmkGIcbeFIg+V7By7VEBNLBZmWABYpSOdFv3aLPd/ckHrwhkS36wkttCiSyYUIKAKqeZwciCJqM7ibj/ZlHyUHRnXUbwLgJ+YwehJzFxotuF/9bf/7EUhHQYWVo+3+SMhg46B2nmFZW6ANWzXq06aiIoioEqd0RqiXY=
- secure: UIZsGf28/ZLxTsxrn+AVOOtrc1tBQoIP4Ym0LiaPbL40QZFdmd8EDGpCPKJGVGY673xZ1FD8AnzrpufOUhqzlVDKlH3+CE6fhqBU9ckTnvngCvx3Tr5RkwMMBPEsdugAZA/BF1KpkRboiPXvrp+3z5Mo4CliBQofh/EYZUCkIziGXUrCiJrkO30IojVonLsq9mhiL5Vd6fHeYfQczqxdbW0Xaq9nm68J6yWyva5wlLsnhiDRZPmnb3CWZUkKIDm2YM9AyZROvHW1qfNL70XyAVNLaJ6sRCT5+0A6s0OkIYbfSDB/hhlaFyw7y7sR2lBCG6RpbIaYOKVR673exonKyamuoP5XhqAZ0avXimB6Es0aGBLdXcQusJzw+x3oji7c4r+987nb0vyzuLx9KpaaeHk8PgcNeenEe//1YtRcffp2LvyCaKAflVQurVrDOTfaSmPakvFm1fBH4U4aEqjQA1lk39yqRu+7+9BsWh5yGdIAlluTI1mU9M1TTr2K4Fj3+yaqlvc2pSjrKXBuGZwcF+3kRxUzu8qiPUmEJmjrv2FBdOObLexTba1ScpW83rwqFunkyEzlUGhNvpDq6jWZ3FfTbOqSWGAS6PLYXXbA0s4j3qXgQZrBKzRi2Swf69gqy4BkQ1jspSLi8pP0p6TEodEm5BVzd+A/CsMMUzxDcwY=
================================================
FILE: AUTHORS.txt
================================================
Authors ordered by first contribution.
Niklas Rämö <inramo@gmail.com>
Indigane <https://github.com/indigane>
Jason Holland <https://github.com/jholland918>
Aslak Hellesøy <https://github.com/aslakhellesoy>
Makhambet Adiyetov <https://github.com/stat92>
Alexis Rouillard <https://github.com/Alexays>
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Muuri
Thanks for the interest in contributing to Muuri! Here you will find some instructions on how to create an issue or a pull request.
## Creating an issue
### Questions
First of all you should check out the existing [questions](https://github.com/haltu/muuri/issues?q=label%3Aquestion%20) and see if your question has been asked/answered already. If not, you can [create a new issue](https://github.com/haltu/muuri/issues/new) and explain the problem you're facing.
### Improvements
Improvement ideas are always welcome! Please check first the existing [ideas](https://github.com/haltu/muuri/issues?utf8=%E2%9C%93&q=label%3Aidea), [features](https://github.com/haltu/muuri/issues?q=label%3Afeature) and [enhancements](https://github.com/haltu/muuri/issues?q=label%3Aenhancement) so that you won't be creating a duplicate issue.
### Bugs
Please [create an issue](https://github.com/haltu/muuri/issues/new) and explain the bug in detail. If possible create a [reduced test case](https://css-tricks.com/reduced-test-cases/) and share a link to it. You can, for example, fork [this CodePen example](https://codepen.io/niklasramo/pen/jyJLGM) and modify it to demonstrate the bug.
## Creating a pull request
1. **Discuss first.**
* The first step should always be [creating a new issue](https://github.com/haltu/muuri/issues/new) and discussing your pull request suggestion with the authors and the community.
* After you get green light it's time to get coding.
2. **Fork the repo and create a new branch for your pull request.**
* [Fork Muuri](https://github.com/haltu/muuri#fork-destination-box).
* Create a new branch for your pull request from the master branch. The name of the pull request branch should start with the id of the issue you opened for the pull request, e.g. `#123-fix-something`.
3. **Setup the development environment.**
* Run `npm install` in the repository's directory.
* You can now run the following commands:
* `npm run build`
* Builds `dist/muuri.js` and `dist/muuri.min.js` from `src` directory.
* `npm run lint`
* Lints all files in `src` directory with ESLint.
* `npm run format`
* Formats all files in `src` directory with Prettier.
* `npm run test`
* Runs unit tests for `dist/muuri.js` and `dist/muuri.min.js` files in Sauce Labs.
* To make this work you need to create an `.env` file the project root, which should contain `SAUCE_USERNAME` and `SAUCE_ACCESS_KEY` variables.
* Launches chrome, firefox and safari by default.
* You can provide arguments to launch specific browsers: `npm run test --chrome --firefox --safari --edge`.
4. **Do the updates in `src` folder.**
* Now is the time to make the actual updates to Muuri.
* Remember scope. Don't refactor things that are not related to the pull request.
* After you're done update unit tests and docs (`README.md`) if necessary.
* Also, if this is your first pull request to Muuri remember to add yourself to the `AUTHORS.txt` file, e.g. `John Doe <https://github.com/johndoe>`.
5. **Format, build and test changes.**
* Run `npm run format`, `npm run build` and finally `npm run test`.
6. **Create the pull request.**
* Do your best to explain what the pull request fixes.
* Mention which issue(s) will be closed by the pull request, e.g. `Closes #123`.
* Request a review from [@niklasramo](https://github.com/niklasramo)
* After your pull request is accepted it will be merged to the [dev branch](https://github.com/haltu/muuri/tree/dev) and released with the next release. If you did only some minor change in the documentation it may be merged directly to the master branch.
7. **You made it! Thank you so much for contributing to Muuri!**
================================================
FILE: LICENSE.md
================================================
Copyright (c) 2015, Haltu Oy
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.
================================================
FILE: README.md
================================================
<h1 align="center">
<a id="muuri" href="#muuri" aria-hidden="true"><img src="https://muuri.dev/muuri-logo.svg" alt="Muuri" width="400" /></a>
</h1>
Muuri is a JavaScript layout engine that allows you to build all kinds of layouts (no kidding!) and make them responsive, sortable, filterable, draggable and/or animated. Comparing to what's out there Muuri is a combination of [Packery](http://packery.metafizzy.co/), [Masonry](http://masonry.desandro.com/), [Isotope](http://isotope.metafizzy.co/) and [Sortable](https://github.com/RubaXa/Sortable). Wanna see it in action? Check out the [demo](https://muuri.dev/) on the website.
**Features**
- Fully customizable layout
- Asynchronous layout calculations in web workers
- Drag & drop (even between grids)
- Auto-scrolling during drag
- Nested grids
- Fast animations
- Filtering
- Sorting
<h2><a id="table-of-contents" href="#table-of-contents" aria-hidden="true">#</a> Table of contents</h2>
- [Motivation](#motivation)
- [Getting started](#getting-started)
- [API](#api)
- [Grid constructor](#grid-constructor)
- [Grid options](#grid-options)
- [Grid methods](#grid-methods)
- [Grid events](#grid-events)
- [Item methods](#item-methods)
- [Credits](#credits)
- [License](#license)
<h2><a id="motivation" href="#motivation" aria-hidden="true">#</a> Motivation</h2>
You can build pretty amazing layouts without a single line of JavaScript these days. However, sometimes (rarely though) CSS just isn't enough, and that's where Muuri comes along. At it's very core Muuri is a _layout engine_ which is limited only by your imagination. You can seriously build _any_ kind of layout, asynchronously in web workers if you wish.
Custom layouts aside, you might need to sprinkle some flare (animation) and/or interactivity (filtering / sorting / drag & drop) on your layout (be it CSS or JS based). Stuff gets complex pretty fast and most of us probably reach for existing libraries to handle the complexity at that point. This is why most of these extra features are built into Muuri's core, so you don't have to go hunting for additional libraries or re-inventing the wheel for the nth time.
The long-term goal of Muuri is to provide a simple (and as low-level as possible) API for building amazing layouts with unmatched performance and _most_ of the complexity abstracted away.
<h2><a id="getting-started" href="#getting-started" aria-hidden="true">#</a> Getting started</h2>
<h3><a id="getting-started-1" href="#getting-started-1" aria-hidden="true">#</a> 1. Get Muuri</h3>
Install via [npm](https://www.npmjs.com/package/muuri):
```bash
npm install muuri
```
Or download:
- [muuri.js](https://cdn.jsdelivr.net/npm/muuri@0.9.5/dist/muuri.js) - for development (not minified, with comments).
- [muuri.min.js](https://cdn.jsdelivr.net/npm/muuri@0.9.5/dist/muuri.min.js) - for production (minified, no comments).
Or link directly:
```html
<script src="https://cdn.jsdelivr.net/npm/muuri@0.9.5/dist/muuri.min.js"></script>
```
<h3><a id="getting-started-2" href="#getting-started-2" aria-hidden="true">#</a> 2. Get Web Animations Polyfill (if needed)</h3>
Muuri uses [Web Animations](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API) to handle all the animations by default. If you need to use Muuri on a browser that does not support Web Animations you need to use a [polyfill](https://github.com/web-animations/web-animations-js).
Install via [npm](https://www.npmjs.com/package/web-animations-js):
```bash
npm install web-animations-js
```
Or download:
- [web-animations.min.js](https://cdn.jsdelivr.net/npm/web-animations-js@2.3.2/web-animations.min.js)
Or link directly:
```html
<script src="https://cdn.jsdelivr.net/npm/web-animations-js@2.3.2/web-animations.min.js"></script>
```
<h3><a id="getting-started-3" href="#getting-started-3" aria-hidden="true">#</a> 3. Add the markup</h3>
- Every grid must have a container element (referred as the _grid element_ from now on).
- Grid items must always consist of at least two elements. The outer element is used for positioning the item and the inner element (first direct child) is used for animating the item's visibility (show/hide methods). You can insert any markup you wish inside the inner item element.
- Note that the class names in the below example are not required by Muuri at all, they're just there for example's sake.
```html
<div class="grid">
<div class="item">
<div class="item-content">
<!-- Safe zone, enter your custom markup -->
This can be anything.
<!-- Safe zone ends -->
</div>
</div>
<div class="item">
<div class="item-content">
<!-- Safe zone, enter your custom markup -->
<div class="my-custom-content">
Yippee!
</div>
<!-- Safe zone ends -->
</div>
</div>
</div>
```
<h3><a id="getting-started-4" href="#getting-started-4" aria-hidden="true">#</a> 4. Add the styles</h3>
- The grid element must be "positioned" meaning that it's CSS position property must be set to _relative_, _absolute_ or _fixed_. Also note that Muuri automatically resizes the grid element's width/height depending on the area the items cover and the layout algorithm configuration.
- The item elements must have their CSS position set to _absolute_.
- The item elements must not have any CSS transitions or animations applied to them, because they might conflict with Muuri's internal animation engine. However, the grid element can have transitions applied to it if you want it to animate when it's size changes after the layout operation.
- You can control the gaps between the items by giving some margin to the item elements.
- One last thing. Never ever set `overflow: auto;` or `overflow: scroll;` to the grid element. Muuri's calculation logic does not account for that and you _will_ see some item jumps when dragging starts. Always use a wrapper element for the grid element where you set the `auto`/`scroll` overflow values.
```css
.grid {
position: relative;
}
.item {
display: block;
position: absolute;
width: 100px;
height: 100px;
margin: 5px;
z-index: 1;
background: #000;
color: #fff;
}
.item.muuri-item-dragging {
z-index: 3;
}
.item.muuri-item-releasing {
z-index: 2;
}
.item.muuri-item-hidden {
z-index: 0;
}
.item-content {
position: relative;
width: 100%;
height: 100%;
}
```
<h3><a id="getting-started-5" href="#getting-started-5" aria-hidden="true">#</a> 5. Fire it up</h3>
The bare minimum configuration is demonstrated below. You must always provide the grid element (or a selector so Muuri can fetch the element for you), everything else is optional.
```javascript
var grid = new Muuri('.grid');
```
You can view this little tutorial demo [here](https://codepen.io/niklasramo/pen/wpwNjK). After that you might want to check some [other demos](https://codepen.io/collection/AWopag/) as well.
<h2><a id="api" href="#api" aria-hidden="true">#</a> API</h2>
<h3><a id="grid-constructor" href="#grid-constructor" aria-hidden="true">#</a> Grid constructor</h3>
`Muuri` is a constructor function and should be always instantiated with the `new` keyword. For the sake of clarity, we refer to a Muuri instance as _grid_ throughout the documentation.
**Syntax**
`Muuri( element, [options] )`
**Parameters**
- **element** — _element_ / _string_
- Default value: `null`.
- You can provide the element directly or use a selector (string) which uses [querySelector()](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector) internally.
- **options** — _object_
- Optional. Check out the [detailed options reference](#grid-options).
**Default options**
The default options are stored in `Muuri.defaultOptions` object, which in it's default state contains the following configuration:
```javascript
{
// Initial item elements
items: '*',
// Default show animation
showDuration: 300,
showEasing: 'ease',
// Default hide animation
hideDuration: 300,
hideEasing: 'ease',
// Item's visible/hidden state styles
visibleStyles: {
opacity: '1',
transform: 'scale(1)'
},
hiddenStyles: {
opacity: '0',
transform: 'scale(0.5)'
},
// Layout
layout: {
fillGaps: false,
horizontal: false,
alignRight: false,
alignBottom: false,
rounding: false
},
layoutOnResize: 150,
layoutOnInit: true,
layoutDuration: 300,
layoutEasing: 'ease',
// Sorting
sortData: null,
// Drag & Drop
dragEnabled: false,
dragContainer: null,
dragHandle: null,
dragStartPredicate: {
distance: 0,
delay: 0
},
dragAxis: 'xy',
dragSort: true,
dragSortHeuristics: {
sortInterval: 100,
minDragDistance: 10,
minBounceBackAngle: 1
},
dragSortPredicate: {
threshold: 50,
action: 'move',
migrateAction: 'move'
},
dragRelease: {
duration: 300,
easing: 'ease',
useDragContainer: true
},
dragCssProps: {
touchAction: 'none',
userSelect: 'none',
userDrag: 'none',
tapHighlightColor: 'rgba(0, 0, 0, 0)',
touchCallout: 'none',
contentZooming: 'none'
},
dragPlaceholder: {
enabled: false,
createElement: null,
onCreate: null,
onRemove: null
},
dragAutoScroll: {
targets: [],
handle: null,
threshold: 50,
safeZone: 0.2,
speed: Muuri.AutoScroller.smoothSpeed(1000, 2000, 2500),
sortDuringScroll: true,
smoothStop: false,
onStart: null,
onStop: null
},
// Classnames
containerClass: 'muuri',
itemClass: 'muuri-item',
itemVisibleClass: 'muuri-item-shown',
itemHiddenClass: 'muuri-item-hidden',
itemPositioningClass: 'muuri-item-positioning',
itemDraggingClass: 'muuri-item-dragging',
itemReleasingClass: 'muuri-item-releasing',
itemPlaceholderClass: 'muuri-item-placeholder'
}
```
You can modify the default options easily:
```javascript
Muuri.defaultOptions.showDuration = 400;
Muuri.defaultOptions.dragSortPredicate.action = 'swap';
```
This is how you would use the options:
```javascript
// Minimum configuration.
var gridA = new Muuri('.grid-a');
// Providing some options.
var gridB = new Muuri('.grid-b', {
items: '.item',
});
```
<h3><a id="grid-options" href="#grid-options" aria-hidden="true">#</a> Grid options</h3>
- [items](#grid-option-items)
- [showDuration](#grid-option-showduration)
- [showEasing](#grid-option-showeasing)
- [hideDuration](#grid-option-hideduration)
- [hideEasing](#grid-option-hideeasing)
- [visibleStyles](#grid-option-visiblestyles)
- [hiddenStyles](#grid-option-hiddenstyles)
- [layout](#grid-option-layout)
- [layoutOnResize](#grid-option-layoutonresize)
- [layoutOnInit](#grid-option-layoutoninit)
- [layoutDuration](#grid-option-layoutduration)
- [layoutEasing](#grid-option-layouteasing)
- [sortData](#grid-option-sortdata)
- [dragEnabled](#grid-option-dragenabled)
- [dragContainer](#grid-option-dragcontainer)
- [dragHandle](#grid-option-draghandle)
- [dragStartPredicate](#grid-option-dragstartpredicate)
- [dragAxis](#grid-option-dragaxis)
- [dragSort](#grid-option-dragsort)
- [dragSortHeuristics](#grid-option-dragsortheuristics)
- [dragSortPredicate](#grid-option-dragsortpredicate)
- [dragRelease](#grid-option-dragrelease)
- [dragCssProps](#grid-option-dragcssprops)
- [dragPlaceholder](#grid-option-dragplaceholder)
- [dragAutoScroll](#grid-option-dragautoscroll)
- [containerClass](#grid-option-containerclass)
- [itemClass](#grid-option-itemclass)
- [itemVisibleClass](#grid-option-itemvisibleclass)
- [itemHiddenClass](#grid-option-itemhiddenclass)
- [itemPositioningClass](#grid-option-itempositioningclass)
- [itemDraggingClass](#grid-option-itemdraggingclass)
- [itemReleasingClass](#grid-option-itemreleasingclass)
- [itemPlaceholderClass](#grid-option-itemplaceholderclass)
<h3><a id="grid-option-items" href="#grid-option-items" aria-hidden="true">#</a> <i>option</i>: items</h3>
The initial item elements, which should be children of the grid element. All elements that are not children of the grid element (e.g. if they are not in the DOM yet) will be appended to the grid element. You can provide an array of elements, [NodeList](https://developer.mozilla.org/en-US/docs/Web/API/NodeList), [HTMLCollection](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection) or a selector (string). If you provide a selector Muuri uses it to filter the current child elements of the container element and sets them as initial items. By default all current child elements of the provided grid element are used as initial items.
- Default value: `'*'`.
- Accepted types: array (of elements), [NodeList](https://developer.mozilla.org/en-US/docs/Web/API/NodeList), [HTMLCollection](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection), string, null.
**Examples**
```javascript
// Use specific items.
var grid = new Muuri(elem, {
items: [elemA, elemB, elemC],
});
// Use node list.
var grid = new Muuri(elem, {
items: elem.querySelectorAll('.item'),
});
// Use selector.
var grid = new Muuri(elem, {
items: '.item',
});
```
<h3><a id="grid-option-showduration" href="#grid-option-showduration" aria-hidden="true">#</a> <i>option</i>: showDuration</h3>
Show animation duration in milliseconds. Set to `0` to disable show animation.
- Default value: `300`.
- Accepted types: number.
**Examples**
```javascript
var grid = new Muuri(elem, {
showDuration: 600,
});
```
<h3><a id="grid-option-showeasing" href="#grid-option-showeasing" aria-hidden="true">#</a> <i>option</i>: showEasing</h3>
Show animation easing. Accepts any valid [Animation easing](https://developer.mozilla.org/en-US/docs/Web/API/AnimationEffectTimingProperties/easing) value.
- Default value: `'ease'`.
- Accepted types: string.
**Examples**
```javascript
var grid = new Muuri(elem, {
showEasing: 'cubic-bezier(0.215, 0.61, 0.355, 1)',
});
```
<h3><a id="grid-option-hideduration" href="#grid-option-hideduration" aria-hidden="true">#</a> <i>option</i>: hideDuration</h3>
Hide animation duration in milliseconds. Set to `0` to disable hide animation.
- Default value: `300`.
- Accepted types: number.
**Examples**
```javascript
var grid = new Muuri(elem, {
hideDuration: 600,
});
```
<h3><a id="grid-option-hideeasing" href="#grid-option-hideeasing" aria-hidden="true">#</a> <i>option</i>: hideEasing</h3>
Hide animation easing. Accepts any valid [Animation easing](https://developer.mozilla.org/en-US/docs/Web/API/AnimationEffectTimingProperties/easing) value.
- Default value: `'ease'`.
- Accepted types: string.
**Examples**
```javascript
var grid = new Muuri(elem, {
hideEasing: 'cubic-bezier(0.215, 0.61, 0.355, 1)',
});
```
<h3><a id="grid-option-visiblestyles" href="#grid-option-visiblestyles" aria-hidden="true">#</a> <i>option</i>: visibleStyles</h3>
The styles that will be applied to all visible items. These styles are also used for the show/hide animations which means that you have to have the same style properties in visibleStyles and hiddenStyles options. Be sure to define all style properties camel cased and without vendor prefixes (Muuri prefixes the properties automatically where needed).
- Default value:
```javascript
{
opacity: 1,
transform: 'scale(1)'
}
```
- Accepted types: object.
**Examples**
```javascript
var grid = new Muuri(elem, {
visibleStyles: {
opacity: 1,
transform: 'rotate(45deg)',
},
hiddenStyles: {
opacity: 0,
transform: 'rotate(-45deg)',
},
});
```
<h3><a id="grid-option-hiddenstyles" href="#grid-option-hiddenstyles" aria-hidden="true">#</a> <i>option</i>: hiddenStyles</h3>
The styles that will be applied to all hidden items. These styles are also used for the show/hide animations which means that you have to have the same style properties in visibleStyles and hiddenStyles options. Be sure to define all style properties camel cased and without vendor prefixes (Muuri prefixes the properties automatically where needed).
- Default value:
```javascript
{
opacity: 0,
transform: 'scale(0.5)'
}
```
- Accepted types: object.
**Examples**
```javascript
var grid = new Muuri(elem, {
visibleStyles: {
opacity: 1,
transform: 'rotate(45deg)',
},
hiddenStyles: {
opacity: 0,
transform: 'rotate(-45deg)',
},
});
```
<h3><a id="grid-option-layout" href="#grid-option-layout" aria-hidden="true">#</a> <i>option</i>: layout</h3>
Define how the items will be positioned. Muuri ships with a configurable layout algorithm which is used by default. It's pretty flexible and suitable for most common situations (lists, grids and even bin packed grids). If that does not fit the bill you can always provide your own layout algorithm (it's not as scary as it sounds).
Muuri supports calculating the layout both synchronously and asynchronously. By default (if you use the default layout algorithm) Muuri will use two shared web workers to compute the layouts asynchronously. In browsers that do not support web workers Muuri will fallback to synchronous layout calculations.
- Default value:
```javascript
{
fillGaps: false,
horizontal: false,
alignRight: false,
alignBottom: false,
rounding: false
}
```
- Accepted types: function, object.
**Provide an _object_ to configure the default layout algorithm with the following properties**
- **fillGaps** — _boolean_
- Default value: `false`.
- When `true` the algorithm goes through every item in order and places each item to the first available free slot, even if the slot happens to be visually _before_ the previous element's slot. Practically this means that the items might not end up visually in order, but there will be less gaps in the grid.
- **horizontal** — _boolean_
- Default value: `false`.
- When `true` the grid works in landscape mode (grid expands to the right). Use for horizontally scrolling sites. When `false` the grid works in "portrait" mode and expands downwards.
- **alignRight** — _boolean_
- Default value: `false`.
- When `true` the items are aligned from right to left.
- **alignBottom** — _boolean_
- Default value: `false`.
- When `true` the items are aligned from the bottom up.
- **rounding** — _boolean_
- Default value: `false`.
- When `true` the item dimensions are rounded to a precision of two decimals for the duration of layout calculations. This procedure stabilizes the layout calculations quite a lot, but also causes a hit on performance. Use only if you see your layout behaving badly, which might happen sometimes (hopefully never) when using relative dimension values.
**Provide a _function_ to use a custom layout algorithm**
When you provide a custom layout function Muuri calls it whenever calculation of layout is necessary. Before calling the layout function Muuri always calculates the current width and height of the grid element and also creates an array of all the items that are part of the layout currently (all _active_ items).
The layout function always receives the following arguments:
- **grid** — _Muuri_
- The grid instance that requested the layout.
- **layoutId** — _number_
- Automatically generated unique id for the layout which is used to keep track of the layout requests and to make sure that the correct layout gets applied at correct time.
- **items** — _array_
- Array of `Muuri.Item` instances. A new array instance is created for each layout so there's no harm in manipulating this if you need to (or using it as is for the layout data object).
- **width** — _number_
- Current width (in pixels) of the grid element (excluding borders, but including padding).
- **height** — _number_
- Current height (in pixels) of the grid element (excluding borders, but including padding).
- **callback** — _function_
- When the layout is calculated and ready to be applied you need to call this callback function and provide a _layout object_ as it's argument. Note that if another layout is requesteded while the current layout is still being calculated (asynchronously) this layout will be ignored.
If the layout function's calculations are asynchronous you can optionally return a cancel function, which Muuri will call if there is a new layout request before the current layout has finished it's calculations.
The layout object, which needs to be provided to the callback, must include the following properties.
- **id** — _number_
- The layout's unique id (must be the `layoutId` provided by Muuri).
- **items** — _array_
- Array of the active item instances that are part of the layout. You can pass the same `items` array here which is provided by Muuri (in case you haven't mutated it). This array items must be identical to the array of items provided by Muuri.
- **slots** — _array_
- Array of the item positions (numbers). E.g. if the items were `[a, b]` this should be `[aLeft, aTop, bLeft, bTop]`. You have to calculate the `left` and `top` position for each item in the provided _items_ array in the same order the items are provided.
- **styles** — _object / null_
- Here you can optionally define all the layout related CSS styles that should be applied to the grid element _just_ before the `layoutStart` event is emitted. E.g. `{width: '100%', height: '200px', minWidth: '200px'}`.
- It's important to keep in mind here that if the grid element's `box-sizing` CSS property is set to `border-box` the element's borders are included in the dimensions. E.g. if you set `{width: '100px', width: '100px'}` here and the element has a `5px` border and `box-sizing` is set to `border-box`, then the _layout's_ effective `width` and `height` (as perceived by Muuri) will be `90px`. So remember to take that into account and add the borders to the dimensions when necessary. If this sounds complicated then just don't set borders directly to the grid element or make sure that grid element's `box-sizing` is set to `content-box` (which is the default value).
Note that you can add additional properties to the layout object if you wish, e.g. the default layout algorithm also stores the layout's width and height (in pixels) to the layout object.
**Examples**
```javascript
// Customize the default layout algorithm.
var grid = new Muuri(elem, {
layout: {
fillGaps: true,
horizontal: true,
alignRight: true,
alignBottom: true,
rounding: true,
},
});
```
```javascript
// Build your own layout algorithm.
var grid = new Muuri(elem, {
layout: function (grid, layoutId, items, width, height, callback) {
var layout = {
id: layoutId,
items: items,
slots: [],
styles: {},
};
// Calculate the slots asynchronously. Note that the timeout is here only
// as an example and does not help at all in the calculations. You should
// offload the calculations to web workers if you want real benefits.
// Also note that doing asynchronous layout is completely optional and you
// can call the callback function synchronously also.
var timerId = window.setTimeout(function () {
var item,
m,
x = 0,
y = 0,
w = 0,
h = 0;
for (var i = 0; i < items.length; i++) {
item = items[i];
x += w;
y += h;
m = item.getMargin();
w = item.getWidth() + m.left + m.right;
h = item.getHeight() + m.top + m.bottom;
layout.slots.push(x, y);
}
w += x;
h += y;
// Set the CSS styles that should be applied to the grid element.
layout.styles.width = w + 'px';
layout.styles.height = h + 'px';
// When the layout is fully computed let's call the callback function and
// provide the layout object as it's argument.
callback(layout);
}, 200);
// If you are doing an async layout you _can_ (if you want to) return a
// function that cancels this specific layout calculations if it's still
// processing/queueing when the next layout is requested.
return function () {
window.clearTimeout(timerId);
};
},
});
```
<h3><a id="grid-option-layoutonresize" href="#grid-option-layoutonresize" aria-hidden="true">#</a> <i>option</i>: layoutOnResize</h3>
Should Muuri automatically trigger `layout` method on window resize? Set to `false` to disable. When a number or `true` is provided Muuri will automatically position the items every time window is resized. The provided number (`true` is transformed to `0`) equals to the amount of time (in milliseconds) that is waited before items are positioned after each window resize event.
- Default value: `150`.
- Accepted types: boolean, number.
**Examples**
```javascript
// No layout on resize.
var grid = new Muuri(elem, {
layoutOnResize: false,
});
```
```javascript
// Layout on resize (instantly).
var grid = new Muuri(elem, {
layoutOnResize: true,
});
```
```javascript
// Layout on resize (with 200ms debounce).
var grid = new Muuri(elem, {
layoutOnResize: 200,
});
```
<h3><a id="grid-option-layoutoninit" href="#grid-option-layoutoninit" aria-hidden="true">#</a> <i>option</i>: layoutOnInit</h3>
Should Muuri trigger `layout` method automatically on init?
- Default value: `true`.
- Accepted types: boolean.
**Examples**
```javascript
var grid = new Muuri(elem, {
layoutOnInit: false,
});
```
<h3><a id="grid-option-layoutduration" href="#grid-option-layoutduration" aria-hidden="true">#</a> <i>option</i>: layoutDuration</h3>
The duration for item's layout animation in milliseconds. Set to `0` to disable.
- Default value: `300`.
- Accepted types: number.
**Examples**
```javascript
var grid = new Muuri(elem, {
layoutDuration: 600,
});
```
<h3><a id="grid-option-layouteasing" href="#grid-option-layouteasing" aria-hidden="true">#</a> <i>option</i>: layoutEasing</h3>
The easing for item's layout animation. Accepts any valid [Animation easing](https://developer.mozilla.org/en-US/docs/Web/API/AnimationEffectTimingProperties/easing) value.
- Default value: `'ease'`.
- Accepted types: string.
**Examples**
```javascript
var grid = new Muuri(elem, {
layoutEasing: 'cubic-bezier(0.215, 0.61, 0.355, 1)',
});
```
<h3><a id="grid-option-sortdata" href="#grid-option-sortdata" aria-hidden="true">#</a> <i>option</i>: sortData</h3>
The sort data getter functions. Provide an object where the key is the name of the sortable attribute and the function returns a value (from the item) by which the items can be sorted.
- Default value: `null`.
- Accepted types: object, null.
**Examples**
```javascript
var grid = new Muuri(elem, {
sortData: {
foo: function (item, element) {
return parseFloat(element.getAttribute('data-foo'));
},
bar: function (item, element) {
return element.getAttribute('data-bar').toUpperCase();
},
},
});
// Refresh sort data whenever an item's data-foo or data-bar changes
grid.refreshSortData();
// Sort the grid by foo and bar.
grid.sort('foo bar');
```
<h3><a id="grid-option-dragenabled" href="#grid-option-dragenabled" aria-hidden="true">#</a> <i>option</i>: dragEnabled</h3>
Should items be draggable?
- Default value: `false`.
- Accepted types: boolean.
**Examples**
```javascript
var grid = new Muuri(elem, {
dragEnabled: true,
});
```
<h3><a id="grid-option-dragcontainer" href="#grid-option-dragcontainer" aria-hidden="true">#</a> <i>option</i>: dragContainer</h3>
The element the dragged item should be appended to for the duration of the drag. If set to `null` (which is also the default value) the grid element will be used.
- Default value: `null`.
- Accepted types: element, null.
**Examples**
```javascript
var grid = new Muuri(elem, {
dragContainer: document.body,
});
```
<h3><a id="grid-option-draghandle" href="#grid-option-draghandle" aria-hidden="true">#</a> <i>option</i>: dragHandle</h3>
The element within the item element that should be used as the drag handle. This should be a CSS selector which will be fed to `element.querySelector()` as is to obtain the handle element when the item is instantiated. If no valid element is found or if this is `null` Muuri will use the item element as the handle.
- Default value: `null`.
- Accepted types: string, null.
**Examples**
```javascript
var grid = new Muuri(elem, {
dragHandle: '.handle',
});
```
<h3><a id="grid-option-dragstartpredicate" href="#grid-option-dragstartpredicate" aria-hidden="true">#</a> <i>option</i>: dragStartPredicate</h3>
A function that determines when the item should start moving when the item is being dragged. By default uses the built-in start predicate which has some configurable options.
- Default value:
```javascript
{
distance: 0,
delay: 0
}
```
- Accepted types: function, object.
If an object is provided the default start predicate handler will be used. You can define the following properties:
- **distance** — _number_
- Default value: `0`.
- How many pixels the user must drag before the drag procedure starts and the item starts moving.
- **delay** — _number_
- Default value: `0`.
- How long (in milliseconds) the user must drag before the dragging starts.
If you provide a function you can customize the drag start logic as you please. When the user starts to drag an item this predicate function will be called until you return `true` or `false`. If you return `true` the item will begin to move whenever the item is dragged. If you return `false` the item will not be moved at all. Note that after you have returned `true` or `false` this function will not be called until the item is released and dragged again.
The predicate function receives two arguments:
- **item** — _Muuri.Item_
- The item that's being dragged.
- **event** — _object_
- Muuri.Dragger event data.
**Examples**
```javascript
// Configure the default predicate
var grid = new Muuri(elem, {
dragStartPredicate: {
distance: 10,
delay: 100,
},
});
```
```javascript
// Provide your own predicate
var grid = new Muuri(elem, {
dragStartPredicate: function (item, e) {
// Start moving the item after the item has been dragged for one second.
if (e.deltaTime > 1000) {
return true;
}
},
});
```
```javascript
// Pro tip: provide your own predicate and fall back to the default predicate.
var grid = new Muuri(elem, {
dragStartPredicate: function (item, e) {
// If this is final event in the drag process, let's prepare the predicate
// for the next round (do some resetting/teardown). The default predicate
// always needs to be called during the final event if there's a chance it
// has been triggered during the drag process because it does some necessary
// state resetting.
if (e.isFinal) {
Muuri.ItemDrag.defaultStartPredicate(item, e);
return;
}
// Prevent first item from being dragged.
if (grid.getItems()[0] === item) {
return false;
}
// For other items use the default drag start predicate.
return Muuri.ItemDrag.defaultStartPredicate(item, e);
},
});
```
<h3><a id="grid-option-dragaxis" href="#grid-option-dragaxis" aria-hidden="true">#</a> <i>option</i>: dragAxis</h3>
Force items to be moved only vertically or horizontally when dragged. Set to `'x'` for horizontal movement and to `'y'` for vertical movement. By default items can be dragged both vertically and horizontally.
- Default value: `'xy'`.
- Accepted types: string.
- Allowed values: `'x'`, `'y'`, `'xy'`.
**Examples**
```javascript
// Move items only horizontally when dragged.
var grid = new Muuri(elem, {
dragAxis: 'x',
});
```
```javascript
// Move items only vertically when dragged.
var grid = new Muuri(elem, {
dragAxis: 'y',
});
```
<h3><a id="grid-option-dragsort" href="#grid-option-dragsort" aria-hidden="true">#</a> <i>option</i>: dragSort</h3>
Should the items be sorted during drag? A simple boolean will do just fine here.
Alternatively you can do some advanced stuff and control within which grids a specific item can be sorted and dragged into. To do that you need to provide a function which receives the dragged item as its first argument and should return an array of grid instances. An important thing to note here is that you need to return _all_ the grid instances you want the dragged item to sort within, even the current grid instance. If you return an empty array the dragged item will not cause sorting at all.
- Default value: `true`.
- Accepted types: boolean, function.
**Examples**
```javascript
// Disable drag sorting.
var grid = new Muuri(elem, {
dragSort: false,
});
```
```javascript
// Multigrid drag sorting.
var gridA = new Muuri(elemA, { dragSort: getAllGrids });
var gridB = new Muuri(elemB, { dragSort: getAllGrids });
var gridC = new Muuri(elemC, { dragSort: getAllGrids });
function getAllGrids(item) {
return [gridA, gridB, gridC];
}
```
<h3><a id="grid-option-dragsortheuristics" href="#grid-option-dragsortheuristics" aria-hidden="true">#</a> <i>option</i>: dragSortHeuristics</h3>
Defines various heuristics so that sorting during drag would be smoother and faster.
- Default value:
```javascript
{
sortInterval: 100,
minDragDistance: 10,
minBounceBackAngle: 1
}
```
- Accepted types: object.
You can define the following properties:
- **sortInterval** — _number_
- Default value: `100`.
- Defines the amount of time the dragged item must be still before `dragSortPredicate` function is called.
- **minDragDistance** — _number_
- Default value: `10`.
- Defines how much (in pixels) the item must be dragged before `dragSortPredicate` can be called.
- **minBounceBackAngle** — _number_
- Default value: `1`.
- Defines the minimum angle (in radians) of the delta vector between the last movement vector and the current movement vector that is required for the dragged item to be allowed to be sorted to it's previous index. The problem this heuristic is trying to solve is the scenario where you drag an item over a much bigger item and the bigger item moves, but it's still overlapping the dragged item after repositioning. Now when you move the dragged item again another sort is triggered and the bigger item moves back to it's previous position. This bouncing back and forth can go on for quite a while and it looks quite erratic. The fix we do here is that, by default, we disallow an item to be moved back to it's previous position, unless it's drag direction changes enough. And what is enough? That's what you can define here. Note that this option works in tandem with `minDragDistance` and needs it to be set to `3` at minimum to be enabled at all.
**Examples**
```javascript
var grid = new Muuri(elem, {
dragEnabled: true,
dragSortHeuristics: {
sortInterval: 10,
minDragDistance: 5,
minBounceBackAngle: Math.PI / 2,
},
});
```
```javascript
// Pro tip: If you want drag sorting happening only on release set a really
// long sortInterval. A bit of a hack, but works.
var grid = new Muuri(elem, {
dragEnabled: true,
dragSortHeuristics: {
sortInterval: 3600000, // 1 hour
},
});
```
<h3><a id="grid-option-dragsortpredicate" href="#grid-option-dragsortpredicate" aria-hidden="true">#</a> <i>option</i>: dragSortPredicate</h3>
Defines the logic for the sort procedure during dragging an item.
- Default value:
```javascript
{
threshold: 50,
action: 'move',
migrateAction: 'move'
}
```
- Accepted types: function, object.
If an object is provided the default sort predicate handler will be used. You can define the following properties:
- **threshold** — _number_
- Default value: `50`.
- Allowed values: `1` - `100`.
- How many percent the intersection area between the dragged item and the compared item should be from the maximum potential intersection area between the items before sorting is triggered.
- **action** — _string_
- Default value: `'move'`.
- Allowed values: `'move'`, `'swap'`.
- Should the dragged item be _moved_ to the new position or should it _swap_ places with the item it overlaps when the drag occurs within the same grid?
- **migrateAction** — _string_
- Default value: `'move'`.
- Allowed values: `'move'`, `'swap'`.
- Should the dragged item be _moved_ to the new position or should it _swap_ places with the item it overlaps when the dragged item is moved to another grid?
Alternatively you can provide your own callback function where you can define your own custom sort logic. The callback function receives two arguments:
- **item** — _Muuri.Item_
- The item that's being dragged.
- **event** — _object_
- Muuri.Dragger event data.
The callback should return a _falsy_ value if sorting should not occur. If, however, sorting should occur the callback should return an object containing the following properties:
- **index** — _number_
- The index where the item should be moved to.
- **grid** — _Muuri_
- The grid where the item should be moved to.
- Defaults to the item's current grid.
- Optional.
- **action** — _string_
- The movement method.
- Default value: `'move'`.
- Allowed values: `'move'` or `'swap'`.
- Optional.
**Examples**
```javascript
// Customize the default predicate.
var grid = new Muuri(elem, {
dragSortPredicate: {
threshold: 90,
action: 'swap',
},
});
```
```javascript
// Provide your own predicate.
var grid = new Muuri(elem, {
dragSortPredicate: function (item, e) {
if (e.deltaTime < 1000) return false;
return {
index: Math.round(e.deltaTime / 1000) % 2 === 0 ? -1 : 0,
action: 'swap',
};
},
});
```
```javascript
// Pro tip: use the default predicate as fallback in your custom predicate.
var grid = new Muuri(elem, {
dragSortPredicate: function (item, e) {
if (item.classList.contains('no-sort')) return false;
return Muuri.ItemDrag.defaultSortPredicate(item, {
action: 'swap',
threshold: 75,
});
},
});
```
<h3><a id="grid-option-dragrelease" href="#grid-option-dragrelease" aria-hidden="true">#</a> <i>option</i>: dragRelease</h3>
- Default value:
```javascript
{
duration: 300,
easing: 'ease',
useDragContainer: true
}
```
- Accepted types: object.
You can define the following properties:
- **duration** — _number_
- Default value: `300`.
- The duration for item's drag release animation. Set to `0` to disable.
- **easing** — _string_
- Default value: `'ease'`.
- The easing for item's drag release animation. Accepts any valid [Animation easing](https://developer.mozilla.org/en-US/docs/Web/API/AnimationEffectTimingProperties/easing) value.
- **useDragContainer** — _boolean_
- Default value: `true`.
- If `true` the item element will remain within the `dragContainer` for the duration of the release process. Otherwise the item element will be inserted within the grid element (if not already inside it) at the beginning of the release process.
**Examples**
```javascript
var grid = new Muuri(elem, {
dragRelease: {
duration: 600,
easing: 'ease-out',
useDragContainer: false,
},
});
```
<h3><a id="grid-option-dragcssprops" href="#grid-option-dragcssprops" aria-hidden="true">#</a> <i>option</i>: dragCssProps</h3>
Drag specific CSS properties that Muuri sets to the draggable item elements. Muuri automatically prefixes the properties before applying them to the element. `touchAction` property is required to be always defined, but the other properties are optional and can be omitted by setting their value to an empty string if you want to e.g. define them via CSS only.
- Default value:
```javascript
{
touchAction: 'none',
userSelect: 'none',
userDrag: 'none',
tapHighlightColor: 'rgba(0, 0, 0, 0)',
touchCallout: 'none',
contentZooming: 'none'
}
```
- Accepted types: object.
You can define the following properties:
- **touchAction** — _string_
- Default value: `'none'`.
- https://developer.mozilla.org/en-US/docs/Web/CSS/touch-action
- **userSelect** — _string_
- Default value: `'none'`.
- https://developer.mozilla.org/en-US/docs/Web/CSS/user-select
- Optional.
- **userDrag** — _string_
- Default value: `'none'`.
- http://help.dottoro.com/lcbixvwm.php
- Optional.
- **tapHighlightColor** — _string_
- Default value: `'rgba(0, 0, 0, 0)'`.
- https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-tap-highlight-color
- Optional.
- **touchCallout** — _string_
- Default value: `'none'`.
- https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-touch-callout
- Optional.
- **contentZooming** — _string_
- Default value: `'none'`.
- https://developer.mozilla.org/en-US/docs/Web/CSS/-ms-content-zooming
- Optional.
**Examples**
```javascript
// Only set the required touch-action CSS property via the options if you for
// example want to set the other props via CSS instead.
var grid = new Muuri(elem, {
dragEnabled: true,
dragCssProps: {
touchAction: 'pan-y',
userSelect: '',
userDrag: '',
tapHighlightColor: '',
touchCallout: '',
contentZooming: '',
},
});
```
<h3><a id="grid-option-dragplaceholder" href="#grid-option-dragplaceholder" aria-hidden="true">#</a> <i>option</i>: dragPlaceholder</h3>
If you want a placeholder item to appear for the duration of an item's drag & drop procedure you can enable and configure it here. The placeholder animation duration is fetched from the grid's `layoutDuration` option and easing from the grid's `layoutEasing` option. Note that a special placeholder class is given to all drag placeholders and is customizable via [itemPlaceholderClass](#itemplaceholderclass-) option.
- Default value:
```javascript
{
enabled: false,
createElement: null,
onCreate: null,
onRemove: null
}
```
- Accepted types: object.
You can define the following properties:
- **enabled** — _boolean_
- Default value: `false`.
- Is the placeholder enabled?
- **createElement** — _function / null_
- Default value: `null`.
- If defined, this method will be used to create the DOM element that is used for the placeholder. By default a new `div` element is created when a placeholder is summoned.
- **onCreate** — _function / null_
- Default value: `null`.
- An optional callback that will be called after a placeholder is created for an item.
- **onRemove** — _function / null_
- Default value: `null`.
- An optional callback that will be called after a placeholder is removed from the grid.
**Examples**
```javascript
// This example showcases how to pool placeholder elements
// for better performance and memory efficiency.
var phPool = [];
var phElem = document.createElement('div');
var grid = new Muuri(elem, {
dragEnabled: true,
dragPlaceholder: {
enabled: true,
createElement(item) {
return phPool.pop() || phElem.cloneNode();
},
onCreate(item, element) {
// If you want to do something after the
// placeholder is fully created, here's
// the place to do it.
},
onRemove(item, element) {
phPool.push(element);
},
},
});
```
<h3><a id="grid-option-dragautoscroll" href="#grid-option-dragautoscroll" aria-hidden="true">#</a> <i>option</i>: dragAutoScroll</h3>
If you want to trigger scrolling on any element during dragging you can enable and configure it here. By default this feature is disabled. When you use this feature it is _highly_ recommended that you create a `fixed` positioned element right under `document.body` and use that as the `dragContainer` for all the dragged items. If you don't do this and a dragged item's parent is auto-scrolled, the dragged item will potentially grow the scrolled element's scroll area to infinity unintentionally.
- Default value:
```javascript
{
targets: [],
handle: null,
threshold: 50,
safeZone: 0.2,
speed: Muuri.AutoScroller.smoothSpeed(1000, 2000, 2500),
sortDuringScroll: true,
smoothStop: false,
onStart: null,
onStop: null
}
```
- Accepted types: object.
You can define the following properties:
- **targets** — _array / function_
- Default value: `[]`.
- Define the DOM elements that should be scrolled during drag. As long as this array is empty there will be no scrolling during drag. To keep it simple you can just provide an array of elements here, in which case Muuri attempts to scroll the elements both vertically and horizontally when possible. If you want more fine-grained control, e.g. scroll an element only on specific axis or prioritize some element over another (handy for cases when there are overlapping elements), you can provide an array of scroll targets (objects). Finally, you can also provide a function which receives the dragged `item` instance as it's argument and which should return an array of scroll targets (elements and/or objects). This way you can provide different configurations for different items.
- **scrollTarget** — _object_
- **element** — _element_ / _window_
- The DOM element to scroll.
- Required.
- **axis** — _number_
- Optional. Defaults to scrolling both axes: `Muuri.AutoScroller.AXIS_X | Muuri.AutoScroller.AXIS_Y`.
- To scroll only x-axis: `Muuri.AutoScroller.AXIS_X`.
- To scroll only y-axis: `Muuri.AutoScroller.AXIS_Y`.
- **priority** — _number_
- Default: `0`.
- A dragged item can only scroll one element horizontally and one element vertically simultaneously. This is an artificial limit to fend off unnecesary complexity, and to avoid awkward situations. In the case where the dragged item overlaps multiple scrollable elements simultaneously and exceeds their scroll thresholds we pick the one that the dragged item overlaps most. However, that's not always the best choice. This is where `priority` comes in. Here you can manually tell Muuri which element to prefer over another in these scenarios. The element with highest priority _always_ wins the fight, in matches with equal priority we determine the winner by the amount of overlap.
- Optional.
- **threshold** — _number / null_
- Default: `null`.
- If defined (a number is provided), this value will override the default threshold for _this scroll target_. Otherwise the default threshold will be used.
- Optional.
- **handle** — _function / null_
- Default value: `null`.
- This property defines size and position of the handle (the rectangle that is compared against the scroll element's threshold). By default (when `null`) the dragged element's dimensions and offsets are used. However, you can provide a function which should return an object containing the handle's client offsets in pixels (`left` and `top`) and dimensions in pixels (`width` and `height`). The function receives the following arguments:
- **item** — _Muuri.Item_
- **itemClientX** — _number_
- **itemClientY** — _number_
- **itemWidth** — _number_
- **itemHeight** — _number_
- **pointerClientX** — _number_
- **pointerClientY** — _number_
- Tip: Use `Muuri.AutoScroller.pointerHandle(pointerSize)` utility method if you want to use the pointer (instead of the element) as the handle.
- **threshold** — _number_
- Default value: `50`.
- Defines the distance (in pixels) from the edge of the scrollable element when scrolling should start, in pixels. If this value is `0` the scrolling will start when the dragged element reaches the scrollable element's edge. Do note that Muuri dynamically adjusts the scroll element's _edge_ for the calculations (when needed).
- **safeZone** — _number_
- Default value: `0.2`.
- Defines the size of the minimum "safe zone" space, an area in the center of the scrollable element that will be guaranteed not trigger scrolling regardless of threshold size and the dragged item's size. This value is a percentage of the scrollable element's size (width and/or height depending on the scroll axes), and should be something between `0` and `1`. So in practice, if you set this to e.g `0.5` the safe zone would be 50% of the scrollable element's width and/or height.
- **speed** — _number / function_
- Default value: `Muuri.AutoScroller.smoothSpeed(1000, 2000, 2500)`.
- Defines the scrolling speed in pixels per second. You can provide either static speed with a `number` or dynamic speed with a `function`. The function is called before every scroll operation and should return the speed (`number`, pixels per second) for the next scroll operation. The function receives three arguments:
- **item** — _Muuri.Item_
- The dragged `Muuri.Item` instance.
- **scrollElement** — _element_ / _window_
- The scrolled element.
- **data** — _object_
- **data.direction** — _number_
- The direction of the scroll, one of the following: `Muuri.AutoScroller.LEFT`, `Muuri.AutoScroller.RIGHT`, `Muuri.AutoScroller.UP`, `Muuri.AutoScroller.DOWN`.
- **data.threshold** — _number_
- The current threshold in pixels.
- **data.distance** — _number_
- The handle rectangle's (as defined in `handle` option) current distance from the edge of the scroll element. E.g, if `direction` is `Muuri.AutoScroller.RIGHT` then distance is `scrollElement.getBoundingClientRect().right - handleRect.right`, and if `direction` is `Muuri.AutoScroller.LEFT` then distance is `handleRect.left - scrollElement.getBoundingClientRect().left`. Can be a negative value too.
- **data.value** — _number_
- The scroll element's current scroll value on the scrolled axis.
- **data.maxValue** — _number_
- The scroll element's maximum scroll value on the scrolled axis.
- **data.duration** — _number_
- How long (in milliseconds) this specific auto-scroll operation has lasted so far.
- **data.speed** — _number_
- The current speed as pixels per second.
- **data.deltaTime** — _number_
- `requestAnimationFrame`'s delta time (in milliseconds).
- **data.isEnding** — _boolean_
- Is the scroll process ending? When this is `true` it means that the associated drag item does not satisfy the threshold anymore. You should now start decreasing the speed towards `0` to allow the item to come to rest smoothly.
- Pro tip: Use `Muuri.AutoScroller.smoothSpeed()` for dynamic speed that provides a smooth scrolling experience. When executed it creates and returns a speed function which you can directly provide for `speed` option. The method _requires_ three arguments (in the following order):
- **maxSpeed** — _number_
- The maximum speed (pixels per second) when the handle's distance to the scroll target's edge is `0` or less.
- **acceleration** — _number_
- How fast the the speed may accelerate (pixels per second).
- **deceleration** — _number_
- How fast the the speed may decelerate (pixels per second).
- **sortDuringScroll** — _boolean_
- Default value: `true`.
- Should the grid items be sorted during auto-scroll or not?
- **smoothStop** — _boolean_
- Default value: `false`.
- When a dragged item is moved out of the threshold area the scroll process is set to _ending_ state. However, it's up to you to decide if the actual scrolling motion is stopped gradually or instantly. By default, when this is `false`, scrolling will stop immediately. If you set this to `true` scrolling will continue until speed drops to `0`. When this option is `enabled` you _must_ handle decelerating the speed to `0` manually within `speed` function, so do not enable this option if you use a static speed value. The default `speed` function handles the deceleration automatically.
- **onStart** — _null / function_
- Default value: `null`.
- Optionally, you can provide a callback that will be called when an item starts auto-scrolling a scroll target. The callback function will receive the following arguments:
- **item** — _Muuri.Item_
- The dragged `Muuri.Item` instance.
- **scrollElement** — _element_ / _window_
- The scrolled element.
- **direction** — _number_
- The direction of the scroll, one of the following: `Muuri.AutoScroller.LEFT`, `Muuri.AutoScroller.RIGHT`, `Muuri.AutoScroller.UP`, `Muuri.AutoScroller.DOWN`.
- **onStop** — _null / function_
- Default value: `null`.
- Optionally, you can provide a callback that will be called when an item stops auto-scrolling a scroll target. The callback function will receive the following arguments:
- **item** — _Muuri.Item_
- The dragged `Muuri.Item` instance.
- **scrollElement** — _element_ / _window_
- The scrolled element.
- **direction** — _number_
- The direction of the scroll, one of the following: `Muuri.AutoScroller.LEFT`, `Muuri.AutoScroller.RIGHT`, `Muuri.AutoScroller.UP`, `Muuri.AutoScroller.DOWN`.
**Examples**
```javascript
// Create a fixed drag container for the dragged items, this is done with JS
// just for example's purposes.
var dragContainer = document.createElement('div');
dragContainer.style.position = 'fixed';
dragContainer.style.left = '0px';
dragContainer.style.top = '0px';
dragContainer.style.zIndex = 1000;
document.body.appendChild(dragContainer);
var grid = new Muuri(elem, {
dragEnabled: true,
dragContainer: dragContainer,
dragAutoScroll: {
targets: [
// Scroll window on both x-axis and y-axis.
{ element: window, priority: 0 },
// Scroll scrollElement (can be any scrollable element) on y-axis only,
// and prefer it over window in conflict scenarios.
{ element: scrollElement, priority: 1, axis: Muuri.AutoScroller.AXIS_Y },
],
// Let's use the dragged item element as the handle.
handle: null,
// Start auto-scroll when the distance from scroll target's edge to dragged
// item is 40px or less.
threshold: 40,
// Make sure the inner 10% of the scroll target's area is always "safe zone"
// which does not trigger auto-scroll.
safeZone: 0.1,
// Let's define smooth dynamic speed.
// Max speed: 2000 pixels per second
// Acceleration: 2700 pixels per second
// Deceleration: 3200 pixels per second.
speed: Muuri.AutoScroller.smoothSpeed(2000, 2700, 3200),
// Let's not sort during scroll.
sortDuringScroll: false,
// Enable smooth stop.
smoothStop: true,
// Finally let's log some data when auto-scroll starts and stops.
onStart: function (item, scrollElement, direction) {
console.log('AUTOSCROLL STARTED', item, scrollElement, direction);
},
onStop: function (item, scrollElement, direction) {
console.log('AUTOSCROLL STOPPED', item, scrollElement, direction);
},
},
});
```
<h3><a id="grid-option-containerclass" href="#grid-option-containerclass" aria-hidden="true">#</a> <i>option</i>: containerClass</h3>
Grid element's class name.
- Default value: `'muuri'`.
- Accepted types: string.
**Examples**
```javascript
var grid = new Muuri(elem, {
containerClass: 'foo',
});
```
<h3><a id="grid-option-itemclass" href="#grid-option-itemclass" aria-hidden="true">#</a> <i>option</i>: itemClass</h3>
Item element's class name.
- Default value: `'muuri-item'`.
- Accepted types: string.
**Examples**
```javascript
var grid = new Muuri(elem, {
itemClass: 'foo-item',
});
```
<h3><a id="grid-option-itemvisibleclass" href="#grid-option-itemvisibleclass" aria-hidden="true">#</a> <i>option</i>: itemVisibleClass</h3>
Visible item's class name.
- Default value: `'muuri-item-shown'`.
- Accepted types: string.
**Examples**
```javascript
var grid = new Muuri(elem, {
itemVisibleClass: 'foo-item-shown',
});
```
<h3><a id="grid-option-itemhiddenclass" href="#grid-option-itemhiddenclass" aria-hidden="true">#</a> <i>option</i>: itemHiddenClass</h3>
Hidden item's class name.
- Default value: `'muuri-item-hidden'`.
- Accepted types: string.
**Examples**
```javascript
var grid = new Muuri(elem, {
itemHiddenClass: 'foo-item-hidden',
});
```
<h3><a id="grid-option-itempositioningclass" href="#grid-option-itempositioningclass" aria-hidden="true">#</a> <i>option</i>: itemPositioningClass</h3>
This class name will be added to the item element for the duration of positioning.
- Default value: `'muuri-item-positioning'`.
- Accepted types: string.
**Examples**
```javascript
var grid = new Muuri(elem, {
itemPositioningClass: 'foo-item-positioning',
});
```
<h3><a id="grid-option-itemdraggingclass" href="#grid-option-itemdraggingclass" aria-hidden="true">#</a> <i>option</i>: itemDraggingClass</h3>
This class name will be added to the item element for the duration of drag.
- Default value: `'muuri-item-dragging'`.
- Accepted types: string.
**Examples**
```javascript
var grid = new Muuri(elem, {
itemDraggingClass: 'foo-item-dragging',
});
```
<h3><a id="grid-option-itemreleasingclass" href="#grid-option-itemreleasingclass" aria-hidden="true">#</a> <i>option</i>: itemReleasingClass</h3>
This class name will be added to the item element for the duration of release.
- Default value: `'muuri-item-releasing'`.
- Accepted types: string.
**Examples**
```javascript
var grid = new Muuri(elem, {
itemReleasingClass: 'foo-item-releasing',
});
```
<h3><a id="grid-option-itemplaceholderclass" href="#grid-option-itemplaceholderclass" aria-hidden="true">#</a> <i>option</i>: itemPlaceholderClass</h3>
This class name will be added to the drag placeholder element.
- Default value: `'muuri-item-placeholder'`.
- Accepted types: string.
**Examples**
```javascript
var grid = new Muuri(elem, {
itemPlaceholderClass: 'foo-item-placeholder',
});
```
<h3><a id="grid-methods" href="#grid-methods" aria-hidden="true">#</a> Grid methods</h3>
- [getElement](#grid-method-getelement)
- [getItem](#grid-method-getitem)
- [getItems](#grid-method-getitems)
- [refreshItems](#grid-method-refreshitems)
- [refreshSortData](#grid-method-refreshsortdata)
- [synchronize](#grid-method-synchronize)
- [layout](#grid-method-layout)
- [add](#grid-method-add)
- [remove](#grid-method-remove)
- [show](#grid-method-show)
- [hide](#grid-method-hide)
- [filter](#grid-method-filter)
- [sort](#grid-method-sort)
- [move](#grid-method-move)
- [send](#grid-method-send)
- [on](#grid-method-on)
- [off](#grid-method-off)
- [destroy](#grid-method-destroy)
<h3><a id="grid-method-getelement" href="#grid-method-getelement" aria-hidden="true">#</a> grid.getElement()</h3>
Get the grid element.
**Returns** — _element_
**Examples**
```javascript
var elem = grid.getElement();
```
<h3><a id="grid-method-getitem" href="#grid-method-getitem" aria-hidden="true">#</a> grid.getItem( target )</h3>
Get a single grid item by element or by index. Target can also be a `Muuri.Item` instance in which case the function returns the item if it exists within related `Muuri` instance. If nothing is found with the provided target, `null` is returned.
**Parameters**
- **target** — _element / number / Muuri.Item_
**Returns** — _Muuri.Item / null_
- Returns the queried item or `null` if no item is found.
**Examples**
```javascript
// Get first item in grid.
var itemA = grid.getItem(0);
// Get item by element reference.
var itemB = grid.getItem(someElement);
```
<h3><a id="grid-method-getitems" href="#grid-method-getitems" aria-hidden="true">#</a> grid.getItems( [targets] )</h3>
Get all items in the grid. Optionally you can provide specific targets (indices or elements).
**Parameters**
- **targets** — _array / element / Muuri.Item / number_
- An array of item instances/elements/indices.
- Optional.
**Returns** — _array_
- Returns the queried items.
**Examples**
```javascript
// Get all items, both active and inactive.
var allItems = grid.getItems();
// Get all active items.
var activeItems = grid.getItems().filter(function (item) {
return item.isActive();
});
// Get all positioning items.
var positioningItems = grid.getItems().filter(function (item) {
return item.isPositioning();
});
// Get the first item.
var firstItem = grid.getItems(0)[0];
// Get specific items by their elements.
var items = grid.getItems([elemA, elemB]);
```
<h3><a id="grid-method-refreshitems" href="#grid-method-refreshitems" aria-hidden="true">#</a> grid.refreshItems( [items], [force] )</h3>
Update the cached dimensions of the instance's items. By default all the items are refreshed, but you can also provide an array of target items as the first argument if you want to refresh specific items. Note that all hidden items are not refreshed by default since their `display` property is `'none'` and their dimensions are therefore not readable from the DOM. However, if you do want to force update hidden item dimensions too you can provide `true` as the second argument, which makes the elements temporarily visible while their dimensions are being read.
**Parameters**
- **items** — _array_
- To target specific items provide an array of item instances. By default all items are targeted.
- Optional.
- **force** — _boolean_
- Set to `true` to read dimensions of hidden items too (and make them visible for the duration of the reading).
- Default: `false`.
- Optional.
**Returns** — _Muuri_
- Returns the grid instance.
**Examples**
```javascript
// Refresh dimensions of all items.
grid.refreshItems();
// Refresh dimensions of specific items.
grid.refreshItems([0, someElem, someItem]);
// Refresh dimensions of specific items and force read their
// dimensions even if they are hidden. Note that this has performance cost.
grid.refreshItems([0, someElem, someHiddenItem], true);
```
<h3><a id="grid-method-refreshsortdata" href="#grid-method-refreshsortdata" aria-hidden="true">#</a> grid.refreshSortData( [items] )</h3>
Refresh the sort data of the grid's items.
**Parameters**
- **items** — _array_
- To target specific items provide an array of item instances. By default all items are targeted.
- Optional.
**Returns** — _Muuri_
- Returns the grid instance.
**Examples**
```javascript
// Refresh the sort data for every item.
grid.refreshSortData();
// Refresh the sort data for specific items.
grid.refreshSortData([0, someElem, someItem]);
```
<h3><a id="grid-method-synchronize" href="#grid-method-synchronize" aria-hidden="true">#</a> grid.synchronize()</h3>
Synchronize the item elements in the DOM to match the order of the items in the grid. This comes handy if you need to keep the DOM structure matched with the order of the items. Note that if an item's element is not currently a child of the grid element (if it is dragged for example) it is ignored and left untouched. The reason why item elements are not kept in sync automatically is that there's rarely a need for that as they are absolutely positioned elements.
**Returns** — _Muuri_
- Returns the grid instance.
**Examples**
```javascript
// Let's say we have to move the first item in the grid as the last.
grid.move(0, -1);
// Now the order of the item elements in the DOM is not in sync anymore
// with the order of the items in the grid. We can sync the DOM with
// synchronize method.
grid.synchronize();
```
<h3><a id="grid-method-layout" href="#grid-method-layout" aria-hidden="true">#</a> grid.layout( [instant], [callback] )</h3>
Calculate item positions and move items to their calculated positions, unless they are already positioned correctly. The grid's height/width (depends on the layout algorithm) is also adjusted according to the position of the items.
**Parameters**
- **instant** — _boolean_
- Should the items be positioned instantly without any possible animation?
- Default value: `false`.
- Optional.
- **callback** — _function_
- A callback function that is called after every item in the layout has finished/aborted positioning.
- Receives two arguments:
- An array of all the items in the layout.
- A boolean indicating if the layout has changed or not.
- Optional.
**Returns** — _Muuri_
- Returns the grid instance.
**Examples**
```javascript
// Layout items.
grid.layout();
// Layout items instantly (without animations).
grid.layout(true);
// Layout all items and define a callback that will be called
// after all items have been animated to their positions.
grid.layout(function (items, hasLayoutChanged) {
// If hasLayoutChanged is `true` it means that there has been another layout
// call before this layout had time to finish positioning all the items.
console.log('layout done!');
});
```
<h3><a id="grid-method-add" href="#grid-method-add" aria-hidden="true">#</a> grid.add( elements, [options] )</h3>
Add new items by providing the elements you wish to add to the grid and optionally provide the index where you want the items to be inserted into. All elements that are not already children of the grid element will be automatically appended to the grid element. If an element has it's CSS display property set to none it will be marked as _inactive_ during the initiation process. As long as the item is _inactive_ it will not be part of the layout, but it will retain it's index. You can activate items at any point with `grid.show()` method. This method will automatically call `grid.layout()` if one or more of the added elements are visible. If only hidden items are added no layout will be called. All the new visible items are positioned without animation during their first layout.
**Parameters**
- **elements** — _array / element_
- An array of DOM elements.
- **options.active** — _boolean / undefined_
- By default (when this option is `undefined`) Muuri will automatically detect from each element's `display` style if the item should be active (visible) or inactive (hidden) on init. If the element's `display` style is `none` then the item will be inactive on init. However, you can also provide a boolean here to force the item to be active (`true`) or inactive (`false`) on init.
- Default value: `undefined`.
- Optional.
- **options.index** — _number_
- The index where you want the items to be inserted in. A value of `-1` will insert the items to the end of the list while `0` will insert the items to the beginning. Note that the DOM elements are always just appended to the grid element regardless of the index value. You can use the `grid.synchronize()` method to arrange the DOM elements to the same order as the items.
- Default value: `-1`.
- Optional.
- **options.layout** — _boolean / function / string_
- By default `grid.layout()` is called at the end of this method. With this argument you can control the layout call. You can disable the layout completely with `false`, or provide a callback function for the layout method, or provide the string `'instant'` to make the layout happen instantly without any animations.
- Default value: `true`.
- Optional.
**Returns** — _array_
- Returns the added items.
**Examples**
```javascript
// Add two new items to the end.
var newItemsA = grid.add([elemA, elemB]);
// Add two new items to the beginning.
var newItemsB = grid.add([elemA, elemB], { index: 0 });
// Skip the automatic layout.
var newItemsC = grid.add([elemA, elemB], { layout: false });
```
<h3><a id="grid-method-remove" href="#grid-method-remove" aria-hidden="true">#</a> grid.remove( items, [options] )</h3>
Remove items from the grid.
**Parameters**
- **items** — _array_
- An array of item instances.
- **options.removeElements** — _boolean_
- Should the associated DOM element be removed from the DOM?
- Default value: `false`.
- Optional.
- **options.layout** — _boolean / function / string_
- By default `grid.layout()` is called at the end of this method. With this argument you can control the layout call. You can disable the layout completely with `false`, or provide a callback function for the layout method, or provide the string `'instant'` to make the layout happen instantly without any animations.
- Default value: `true`.
- Optional.
**Returns** — _array_
- Returns the removed items. Note that the removal process also _destroys_ the items so they can not be reused e.g. in another grid.
**Examples**
```javascript
// Remove the first item, but keep the element in the DOM.
var removedItemsA = grid.remove(grid.getItems(0));
// Remove items and the associated elements.
var removedItemsB = grid.remove([itemA, itemB], { removeElements: true });
// Skip the layout.
var removedItemsC = grid.remove([itemA, itemB], { layout: false });
```
<h3><a id="grid-method-show" href="#grid-method-show" aria-hidden="true">#</a> grid.show( items, [options] )</h3>
Show the targeted items.
**Parameters**
- **items** — _array_
- An array of item instances.
- **options.instant** — _boolean_
- Should the items be shown instantly without any possible animation?
- Default value: `false`.
- Optional.
- **options.syncWithLayout** — _boolean_
- Should we wait for the next layout's calculations (which are potentially async) to finish before starting the show animations? By default this option is enabled so that the show animations are triggered in sync with the layout animations. If that's not needed set this to `false` and the show animations will begin immediately.
- Default value: `true`.
- Optional.
- **options.onFinish** — _function_
- A callback function that is called after the items are shown.
- Optional.
- **options.layout** — _boolean / function / string_
- By default `grid.layout()` is called at the end of this method. With this argument you can control the layout call. You can disable the layout completely with `false`, or provide a callback function for the layout method, or provide the string `'instant'` to make the layout happen instantly without any animations.
- Default value: `true`.
- Optional.
**Returns** — _Muuri_
- Returns the grid instance.
**Examples**
```javascript
// Show items with animation (if any).
grid.show([itemA, itemB]);
// Show items instantly without animations.
grid.show([itemA, itemB], { instant: true });
// Show items with callback (and with animations if any).
grid.show([itemA, itemB], {
onFinish: function (items) {
console.log('items shown!');
},
});
```
<h3><a id="grid-method-hide" href="#grid-method-hide" aria-hidden="true">#</a> grid.hide( items, [options] )</h3>
Hide the targeted items.
**Parameters**
- **items** — _array_
- An array of item instances.
- **options.instant** — _boolean_
- Should the items be hidden instantly without any possible animation?
- Default value: `false`.
- Optional.
- **options.syncWithLayout** — _boolean_
- Should we wait for the next layout's calculations (which are potentially async) to finish before starting the hide animations? By default this option is enabled so that the hide animations are triggered in sync with the layout animations. If that's not needed set this to `false` and the hide animations will begin immediately.
- Default value: `true`.
- Optional.
- **options.onFinish** — _function_
- A callback function that is called after the items are hidden.
- Optional.
- **options.layout** — _boolean / function / string_
- By default `grid.layout()` is called at the end of this method. With this argument you can control the layout call. You can disable the layout completely with `false`, or provide a callback function for the layout method, or provide the string `'instant'` to make the layout happen instantly without any animations.
- Default value: `true`.
- Optional.
**Returns** — _Muuri_
- Returns the grid instance.
**Examples**
```javascript
// Hide items with animation.
grid.hide([itemA, itemB]);
// Hide items instantly without animations.
grid.hide([itemA, itemB], { instant: true });
// Hide items and call the callback function after
// all items are hidden.
grid.hide([itemA, itemB], {
onFinish: function (items) {
console.log('items hidden!');
},
});
```
<h3><a id="grid-method-filter" href="#grid-method-filter" aria-hidden="true">#</a> grid.filter( predicate, [options] )</h3>
Filter items. Expects at least one argument, a predicate, which should be either a function or a string. The predicate callback is executed for every item in the grid. If the return value of the predicate is truthy the item in question will be shown and otherwise hidden. The predicate callback receives the item instance as it's argument. If the predicate is a string it is considered to be a selector and it is checked against every item element in the grid with the native element.matches() method. All the matching items will be shown and others hidden.
**Parameters**
- **predicate** — _function / string_
- A predicate callback or a selector.
- **options.instant** — _boolean_
- Should the items be shown/hidden instantly without any possible animation?
- Default value: `false`.
- Optional.
- **options.syncWithLayout** — _boolean_
- Should we wait for the next layout's calculations (which are potentially async) to finish before starting the visibility animations? By default this option is enabled so that the visibility animations are triggered in sync with the layout animations. If that's not needed set this to `false` and the visibility animations will begin immediately.
- Default value: `true`.
- Optional.
- **options.onFinish** — _function_
- An optional callback function that is called after all the items are shown/hidden.
- Optional.
- **options.layout** — _boolean / function / string_
- By default `grid.layout()` is called at the end of this method. With this argument you can control the layout call. You can disable the layout completely with `false`, or provide a callback function for the layout method, or provide the string `'instant'` to make the layout happen instantly without any animations.
- Default value: `true`.
- Optional.
**Returns** — _Muuri_
- Returns the grid instance.
**Examples**
```javascript
// Show all items that have the attribute "data-foo".
grid.filter(function (item) {
return item.getElement().hasAttribute('data-foo');
});
// Or simply just...
grid.filter('[data-foo]');
// Show all items that have a class foo.
grid.filter('.foo');
```
<h3><a id="grid-method-sort" href="#grid-method-sort" aria-hidden="true">#</a> grid.sort( comparer, [options] )</h3>
Sort items. There are three ways to sort the items. The first is simply by providing a function as the comparer which works almost identically to [native array sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort). The only difference is that the sort is always stable. Alternatively you can sort by the sort data you have provided in the grid's options. Just provide the sort data key(s) as a string (separated by space) and the items will be sorted based on the provided sort data keys. Lastly you have the opportunity to provide a presorted array of items which will be used to sync the internal items array in the same order.
**Parameters**
- **comparer** — _array / function / string_
- Provide a comparer function, sort data keys as a string (separated with space) or a pre-sorted array of items. When you provide a pre-sorted array of items you _must_ make sure that it contains _exactly_ the same item instances as exists currently in `grid._items` (retrievable safely via `grid.getItems()`), only change the order of items. Muuri does not validate the array of items you provide due to performance reasons.
- **options.descending** — _boolean_
- By default the items are sorted in ascending order. If you want to sort them in descending order set this to `true`. Note that this option has no effect when you provide a pre-sorted array of items.
- Default value: `false`.
- Optional.
- **options.layout** — _boolean / function / string_
- By default `grid.layout()` is called at the end of this method. With this argument you can control the layout call. You can disable the layout completely with `false`, or provide a callback function for the layout method, or provide the string `'instant'` to make the layout happen instantly without any animations.
- Default value: `true`.
- Optional.
**Returns** — _Muuri_
- Returns the grid instance.
**Examples**
```javascript
// Sort items by data-id attribute value (ascending).
grid.sort(function (itemA, itemB) {
var aId = parseInt(itemA.getElement().getAttribute('data-id'));
var bId = parseInt(itemB.getElement().getAttribute('data-id'));
return aId - bId;
});
// Sort items with a presorted array of items.
grid.sort(grid.getItems().reverse());
// Sort items using the sort data keys (ascending).
grid.sort('foo bar');
// Sort items using the sort data keys (descending).
grid.sort('foo bar', { descending: true });
// Sort items using the sort data keys. Sort some keys
// ascending and some keys descending.
grid.sort('foo bar:desc');
```
<h3><a id="grid-method-move" href="#grid-method-move" aria-hidden="true">#</a> grid.move( item, position, [options] )</h3>
Move an item to another position in the grid.
**Parameters**
- **item** — _element / Muuri.Item / number_
- Item instance, element or index.
- **position** — _element / Muuri.Item / number_
- Item instance, element or index.
- **options.action** — _string_
- Accepts the following values:
- `'move'`: moves item in place of another item.
- `'swap'`: swaps position of items.
- Default value: `'move'`.
- Optional.
- **options.layout** — _boolean / function / string_
- By default `grid.layout()` is called at the end of this method. With this argument you can control the layout call. You can disable the layout completely with `false`, or provide a callback function for the layout method, or provide the string `'instant'` to make the layout happen instantly without any animations.
- Default value: `true`.
- Optional.
**Returns** — _Muuri_
- Returns the grid instance.
**Examples**
```javascript
// Move elemA to the index of elemB.
grid.move(elemA, elemB);
// Move the first item in the grid as the last.
grid.move(0, -1);
// Swap positions of elemA and elemB.
grid.move(elemA, elemB, { action: 'swap' });
// Swap positions of the first and the last item.
grid.move(0, -1, { action: 'swap' });
```
<h3><a id="grid-method-send" href="#grid-method-send" aria-hidden="true">#</a> grid.send( item, grid, position, [options] )</h3>
Move an item into another grid.
**Parameters**
- **item** — _element / Muuri.Item / number_
- The item that should be moved. You can define the item with an item instance, element or index.
- **grid** — _Muuri_
- The grid where the item should be moved to.
- **position** — _element / Muuri.Item / number_
- To which position should the item be placed to in the new grid? You can define the position with an item instance, element or index.
- **options.appendTo** — _element_
- Which element the item element should be appended to for the duration of the layout animation?
- Default value: `document.body`.
- **options.layoutSender** — _boolean / function / string_
- By default `grid.layout()` is called for the sending grid at the end of this method. With this argument you can control the layout call. You can disable the layout completely with `false`, or provide a callback function for the layout method, or provide the string `'instant'` to make the layout happen instantly without any animations.
- Default value: `true`.
- Optional.
- **options.layoutReceiver** — _boolean / function / string_
- By default `grid.layout()` is called for the receiving grid at the end of this method. With this argument you can control the layout call. You can disable the layout completely with `false`, or provide a callback function for the layout method, or provide the string `'instant'` to make the layout happen instantly without any animations.
- Default value: `true`.
- Optional.
**Returns** — _Muuri_
- Returns the grid instance.
**Examples**
```javascript
// Move the first item of gridA as the last item of gridB.
// The sent item will be appended to document.body.
gridA.send(0, gridB, -1);
// Move the first item of gridA as the last item of gridB.
// The sent item will be appended to someElem.
gridA.send(0, gridB, -1, {
appendTo: someElem,
});
// Do something after the item has been sent and the layout
// processes have finished.
gridA.send(0, gridB, -1, {
layoutSender: function (isAborted, items) {
// Do your thing here...
},
layoutReceiver: function (isAborted, items) {
// Do your other thing here...
},
});
```
<h3><a id="grid-method-on" href="#grid-method-on" aria-hidden="true">#</a> grid.on( event, listener )</h3>
Bind an event listener.
**Parameters**
- **event** — _string_
- **listener** — _function_
**Returns** — _Muuri_
- Returns the grid instance.
**Examples**
```javascript
grid.on('layoutEnd', function (items) {
console.log(items);
});
```
<h3><a id="grid-method-off" href="#grid-method-off" aria-hidden="true">#</a> grid.off( event, listener )</h3>
Unbind an event listener.
**Parameters**
- **event** — _string_
- **listener** — _function_
**Returns** — _Muuri_
- Returns the grid instance.
**Examples**
```javascript
function onLayoutEnd(items) {
console.log(items);
}
// Start listening to some event.
grid.on('layoutEnd', onLayoutEnd);
/// ...sometime later -> unbind listener.
grid.off('layoutEnd', onLayoutEnd);
```
<h3><a id="grid-method-destroy" href="#grid-method-destroy" aria-hidden="true">#</a> grid.destroy( [removeElements] )</h3>
Destroy the grid.
**Parameters**
- **removeElements** — _boolean_
- Should the item elements be removed or not?
- Default value: `false`.
- Optional.
**Returns** — _Muuri_
- Returns the grid instance.
**Examples**
```javascript
// Destroy the instance, but keep
// item element in the DOM.
grid.destroy();
```
```javascript
// Destroy the instance and remove
// the item elements from the DOM.
grid.destroy(true);
```
<h3><a id="grid-events" href="#grid-events" aria-hidden="true">#</a> Grid events</h3>
- [synchronize](#grid-event-synchronize)
- [layoutStart](#grid-event-layoutstart)
- [layoutEnd](#grid-event-layoutend)
- [layoutAbort](#grid-event-layoutabort)
- [add](#grid-event-add)
- [remove](#grid-event-remove)
- [showStart](#grid-event-showstart)
- [showEnd](#grid-event-showend)
- [hideStart](#grid-event-hidestart)
- [hideEnd](#grid-event-hideend)
- [filter](#grid-event-filter)
- [sort](#grid-event-sort)
- [move](#grid-event-move)
- [send](#grid-event-send)
- [beforeSend](#grid-event-beforesend)
- [receive](#grid-event-receive)
- [beforeReceive](#grid-event-beforereceive)
- [dragInit](#grid-event-draginit)
- [dragStart](#grid-event-dragstart)
- [dragMove](#grid-event-dragmove)
- [dragScroll](#grid-event-dragscroll)
- [dragEnd](#grid-event-dragend)
- [dragReleaseStart](#grid-event-dragreleasestart)
- [dragReleaseEnd](#grid-event-dragreleaseend)
- [destroy](#grid-event-destroy)
<h3><a id="grid-event-synchronize" href="#grid-event-synchronize" aria-hidden="true">#</a> <i>event</i>: synchronize</h3>
Triggered after item elements are synchronized via `grid.synchronize()`.
**Examples**
```javascript
grid.on('synchronize', function () {
console.log('Synced!');
});
```
<h3><a id="grid-event-layoutstart" href="#grid-event-layoutstart" aria-hidden="true">#</a> <i>event</i>: layoutStart</h3>
Triggered when the the layout procedure begins. More specifically, this event is emitted right after new _layout_ has been generated, internal item positions updated and grid element's dimensions updated. After this event is emitted the items in the layout will be positioned to their new positions. So if you e.g. want to react to grid element dimension changes this is a good place to do that.
**Arguments**
- **items** — _array_
- The items that are about to be positioned.
- **isInstant** — _boolean_
- Was the layout called with `instant` flag or not.
**Examples**
```javascript
grid.on('layoutStart', function (items, isInstant) {
console.log(items, isInstant);
});
```
<h3><a id="grid-event-layoutend" href="#grid-event-layoutend" aria-hidden="true">#</a> <i>event</i>: layoutEnd</h3>
Triggered after the layout procedure has finished, successfully. Note that if you abort a layout procedure by calling `grid.layout()` _before_ items have finished positioning, this event will not be emitted for the aborted layout procedure. In such a case `layoutAbort` will be emitted instead.
**Arguments**
- **items** — _array_
- The items that were positioned. Note that these items are always identical to what the _layoutStart_ event's callback receives as it's argument.
**Examples**
```javascript
grid.on('layoutEnd', function (items) {
console.log(items);
// For good measure you might want to filter out all the non-active items,
// because it's techniclly possible that some of the items are
// destroyed/hidden when we receive this event.
var activeItems = items.filter(function (item) {
return item.isActive();
});
});
```
<h3><a id="grid-event-layoutabort" href="#grid-event-layoutabort" aria-hidden="true">#</a> <i>event</i>: layoutAbort</h3>
Triggered if you start a new layout process (`grid.layout()`) while the current layout process is still busy positioning items. Note that this event is not triggered if you start a new layout process while the layout is being computed and the items have not yet started positioning.
**Arguments**
- **items** — _array_
- The items that were attempted to be positioned. Note that these items are always identical to what the _layoutStart_ event's callback receives as it's argument.
**Examples**
```javascript
grid.on('layoutAbort', function (items) {
console.log(items);
// For good measure you might want to filter out all the non-active items,
// because it's techniclly possible that some of the items are destroyed or
// hidden when we receive this event.
var activeItems = items.filter(function (item) {
return item.isActive();
});
});
```
<h3><a id="grid-event-add" href="#grid-event-add" aria-hidden="true">#</a> <i>event</i>: add</h3>
Triggered after `grid.add()` is called.
**Arguments**
- **items** — _array_
- The items that were successfully added.
**Examples**
```javascript
grid.on('add', function (items) {
console.log(items);
});
```
<h3><a id="grid-event-remove" href="#grid-event-remove" aria-hidden="true">#</a> <i>event</i>: remove</h3>
Triggered after `grid.remove()` is called.
**Arguments**
- **items** — _array_
- The items that were successfully removed.
- **indices** — _array_
- Indices of the items that were successfully removed.
**Examples**
```javascript
grid.on('remove', function (items, indices) {
console.log(items, indices);
});
```
<h3><a id="grid-event-showstart" href="#grid-event-showstart" aria-hidden="true">#</a> <i>event</i>: showStart</h3>
Triggered after `grid.show()` is called, just before the items are shown.
**Arguments**
- **items** — _array_
- The items that are about to be shown.
**Examples**
```javascript
grid.on('showStart', function (items) {
console.log(items);
});
```
<h3><a id="grid-event-showend" href="#grid-event-showend" aria-hidden="true">#</a> <i>event</i>: showEnd</h3>
Triggered after `grid.show()` is called, after the items are shown.
**Arguments**
- **items** — _array_
- The items that were successfully shown without interruptions. If you, for example, call `grid.hide()` to some of the items that are currently being shown, those items will be omitted from this argument.
**Examples**
```javascript
grid.on('showEnd', function (items) {
console.log(items);
});
```
<h3><a id="grid-event-hidestart" href="#grid-event-hidestart" aria-hidden="true">#</a> <i>event</i>: hideStart</h3>
Triggered after `grid.hide()` is called, just before the items are hidden.
**Arguments**
- **items** — _array_
- The items that are about to be hidden.
**Examples**
```javascript
grid.on('hideStart', function (items) {
console.log(items);
});
```
<h3><a id="grid-event-hideend" href="#grid-event-hideend" aria-hidden="true">#</a> <i>event</i>: hideEnd</h3>
Triggered after `grid.hide()` is called, after the items are hidden.
**Arguments**
- **items** — _array_
- The items that were successfully hidden without interruptions. If you, for example, call `grid.show()` to some of the items that are currently being hidden, those items will be omitted from this argument.
**Examples**
```javascript
grid.on('hideEnd', function (items) {
console.log(items);
});
```
<h3><a id="grid-event-filter" href="#grid-event-filter" aria-hidden="true">#</a> <i>event</i>: filter</h3>
Triggered after `grid.filter()` is called.
**Arguments**
- **shownItems** — _array_
- The items that are shown.
- **hiddenItems** — _array_
- The items that are hidden.
**Examples**
```javascript
grid.on('filter', function (shownItems, hiddenItems) {
console.log(shownItems);
console.log(hiddenItems);
});
```
<h3><a id="grid-event-sort" href="#grid-event-sort" aria-hidden="true">#</a> <i>event</i>: sort</h3>
Triggered after `grid.sort()` is called.
**Arguments**
- **currentOrder** — _array_
- All items in their current order.
- **previousOrder** — _array_
- All items in their previous order.
**Examples**
```javascript
grid.on('sort', function (currentOrder, previousOrder) {
console.log(currentOrder);
console.log(previousOrder);
});
```
<h3><a id="grid-event-move" href="#grid-event-move" aria-hidden="true">#</a> <i>event</i>: move</h3>
Triggered after `grid.move()` is called or when the grid is sorted during drag. Note that this is event not triggered when an item is dragged into another grid.
**Arguments**
- **data** — _object_
- **data.item** — _Muuri.Item_
- The item that was moved.
- **data.fromIndex** — _number_
- The index the item was moved from.
- **data.toIndex** — _number_
- The index the item was moved to.
- **data.action** — _string_
- "move" or "swap".
**Examples**
```javascript
grid.on('move', function (data) {
console.log(data);
});
```
<h3><a id="grid-event-send" href="#grid-event-send" aria-hidden="true">#</a> <i>event</i>: send</h3>
Triggered for the originating grid in the end of the _send process_ (after `grid.send()` is called or when an item is dragged into another grid). Note that this event is called _before_ the item's layout starts.
**Arguments**
- **data** — _object_
- **data.item** — _Muuri.Item_
- The item that was sent.
- **data.fromGrid** — _Muuri_
- The grid the item was sent from.
- **data.fromIndex** — _number_
- The index the item was moved from.
- **data.toGrid** — _Muuri_
- The grid the item was sent to.
- **data.toIndex** — _number_
- The index the item was moved to.
**Examples**
```javascript
grid.on('send', function (data) {
console.log(data);
});
```
<h3><a id="grid-event-beforesend" href="#grid-event-beforesend" aria-hidden="true">#</a> <i>event</i>: beforeSend</h3>
Triggered for the originating grid in the beginning of the _send process_ (after `grid.send()` is called or when an item is dragged into another grid). This event is highly useful in situations where you need to manipulate the sent item (freeze it's dimensions for example) before it is appended to it's temporary layout container as defined in [send method options](#gridsend-item-grid-position-options-).
**Arguments**
- **data** — _object_
- **data.item** — _Muuri.Item_
- The item that was sent.
- **data.fromGrid** — _Muuri_
- The grid the item was sent from.
- **data.fromIndex** — _number_
- The index the item was moved from.
- **data.toGrid** — _Muuri_
- The grid the item was sent to.
- **data.toIndex** — _number_
- The index the item was moved to.
**Examples**
```javascript
grid.on('beforeSend', function (data) {
console.log(data);
});
```
<h3><a id="grid-event-receive" href="#grid-event-receive" aria-hidden="true">#</a> <i>event</i>: receive</h3>
Triggered for the receiving grid in the end of the _send process_ (after `grid.send()` is called or when an item is dragged into another grid). Note that this event is called _before_ the item's layout starts.
**Arguments**
- **data** — _object_
- **data.item** — _Muuri.Item_
- The item that was sent.
- **data.fromGrid** — _Muuri_
- The grid the item was sent from.
- **data.fromIndex** — _number_
- The index the item was moved from.
- **data.toGrid** — _Muuri_
- The grid the item was sent to.
- **data.toIndex** — _number_
- The index the item was moved to.
**Examples**
```javascript
grid.on('receive', function (data) {
console.log(data);
});
```
<h3><a id="grid-event-beforereceive" href="#grid-event-beforereceive" aria-hidden="true">#</a> <i>event</i>: beforeReceive</h3>
Triggered for the receiving grid in the beginning of the _send process_ (after `grid.send()` is called or when an item is dragged into another grid). This event is highly useful in situations where you need to manipulate the received item (freeze it's dimensions for example) before it is appended to it's temporary layout container as defined in [send method options](#gridsend-item-grid-position-options-).
**Arguments**
- **data** — _object_
- **data.item** — _Muuri.Item_
- The item that was sent.
- **data.fromGrid** — _Muuri_
- The grid the item was sent from.
- **data.fromIndex** — _number_
- The index the item was moved from.
- **data.toGrid** — _Muuri_
- The grid the item was sent to.
- **data.toIndex** — _number_
- The index the item was moved to.
**Examples**
```javascript
grid.on('beforeReceive', function (data) {
console.log(data);
});
```
<h3><a id="grid-event-draginit" href="#grid-event-draginit" aria-hidden="true">#</a> <i>event</i>: dragInit</h3>
Triggered in the beginning of the _drag start_ process when dragging of an item begins. This event is highly useful in situations where you need to manipulate the dragged item (freeze it's dimensions for example) before it is appended to the [dragContainer](#dragcontainer-).
**Arguments**
- **item** — _Muuri.Item_
- The dragged item.
- **event** — _object_
- Muuri.Dragger event data.
**Examples**
```javascript
grid.on('dragInit', function (item, event) {
console.log(event);
console.log(item);
});
```
<h3><a id="grid-event-dragstart" href="#grid-event-dragstart" aria-hidden="true">#</a> <i>event</i>: dragStart</h3>
Triggered in the end of the _drag start_ process when dragging of an item begins.
**Arguments**
- **item** — _Muuri.Item_
- The dragged item.
- **event** — _object_
- Muuri.Dragger event data.
**Examples**
```javascript
grid.on('dragStart', function (item, event) {
console.log(event);
console.log(item);
});
```
<h3><a id="grid-event-dragmove" href="#grid-event-dragmove" aria-hidden="true">#</a> <i>event</i>: dragMove</h3>
Triggered every time a dragged item is _moved_. Note that Muuri has an automatic throttling system which makes sure that this event is triggered at maximum once in an animation frame.
**Arguments**
- **item** — _Muuri.Item_
- The dragged item.
- **event** — _object_
- Muuri.Dragger event data.
**Examples**
```javascript
grid.on('dragMove', function (item, event) {
console.log(event);
console.log(item);
});
```
<h3><a id="grid-event-dragscroll" href="#grid-event-dragscroll" aria-hidden="true">#</a> <i>event</i>: dragScroll</h3>
Triggered when any of the scroll parents of a dragged item is scrolled.
**Arguments**
- **item** — _Muuri.Item_
- The dragged item.
- **event** — _object_
- Scroll event data.
**Examples**
```javascript
grid.on('dragScroll', function (item, event) {
console.log(event);
console.log(item);
});
```
<h3><a id="grid-event-dragend" href="#grid-event-dragend" aria-hidden="true">#</a> <i>event</i>: dragEnd</h3>
Triggered when dragged item is released and the drag process ends.
**Arguments**
- **item** — _Muuri.Item_
- The dragged item.
- **event** — _object_
- Muuri.Dragger event data.
**Examples**
```javascript
grid.on('dragEnd', function (item, event) {
console.log(event);
console.log(item);
});
```
<h3><a id="grid-event-dragreleasestart" href="#grid-event-dragreleasestart" aria-hidden="true">#</a> <i>event</i>: dragReleaseStart</h3>
Triggered when a dragged item is released (always after `dragEnd` event).
**Arguments**
- **item** — _Muuri.Item_
- The released item.
**Examples**
```javascript
grid.on('dragReleaseStart', function (item) {
console.log(item);
});
```
<h3><a id="grid-event-dragreleaseend" href="#grid-event-dragreleaseend" aria-hidden="true">#</a> <i>event</i>: dragReleaseEnd</h3>
Triggered after released item has finished it's position animation.
**Arguments**
- **item** — _Muuri.Item_
- The released item.
**Examples**
```javascript
grid.on('dragReleaseEnd', function (item) {
console.log(item);
});
```
<h3><a id="grid-event-destroy" href="#grid-event-destroy" aria-hidden="true">#</a> <i>event</i>: destroy</h3>
Triggered after grid is destroyed.
**Examples**
```javascript
grid.on('destroy', function () {
console.log('Muuri is no more...');
});
```
<h3><a id="item-methods" href="#item-methods" aria-hidden="true">#</a> Item methods</h3>
- [getGrid](#item-method-getgrid)
- [getElement](#item-method-getelement)
- [getWidth](#item-method-getwidth)
- [getHeight](#item-method-getheight)
- [getMargin](#item-method-getmargin)
- [getPosition](#item-method-getposition)
- [isActive](#item-method-isactive)
- [isVisible](#item-method-isvisible)
- [isShowing](#item-method-isshowing)
- [isHiding](#item-method-ishiding)
- [isPositioning](#item-method-ispositioning)
- [isDragging](#item-method-isdragging)
- [isReleasing](#item-method-isreleasing)
- [isDestroyed](#item-method-isdestroyed)
<h3><a id="item-method-getgrid" href="#item-method-getgrid" aria-hidden="true">#</a> item.getGrid()</h3>
Get the grid instance the item belongs to.
**Returns** — _Muuri_
**Examples**
```javascript
var grid = item.getGrid();
```
<h3><a id="item-method-getelement" href="#item-method-getelement" aria-hidden="true">#</a> item.getElement()</h3>
Get the item element.
**Returns** — _element_
**Examples**
```javascript
var elem = item.getElement();
```
<h3><a id="item-method-getwidth" href="#item-method-getwidth" aria-hidden="true">#</a> item.getWidth()</h3>
Get item element's cached width (in pixels). The returned value includes the element's paddings and borders.
**Returns** — _number_
**Examples**
```javascript
var width = item.getWidth();
```
<h3><a id="item-method-getheight" href="#item-method-getheight" aria-hidden="true">#</a> item.getHeight()</h3>
Get item element's cached height (in pixels). The returned value includes the element's paddings and borders.
**Returns** — _number_
**Examples**
```javascript
var height = item.getHeight();
```
<h3><a id="item-method-getmargin" href="#item-method-getmargin" aria-hidden="true">#</a> item.getMargin()</h3>
Get item element's cached margins (in pixels).
**Returns** — _object_
- **obj.left** — _number_
- **obj.right** — _number_
- **obj.top** — _number_
- **obj.bottom** — _number_
**Examples**
```javascript
var margin = item.getMargin();
```
<h3><a id="item-method-getposition" href="#item-method-getposition" aria-hidden="true">#</a> item.getPosition()</h3>
Get item element's cached position (in pixels, relative to the grid element).
**Returns** — _object_
- **obj.left** — _number_
- **obj.top** — _number_
**Examples**
```javascript
var position = item.getPosition();
```
<h3><a id="item-method-isactive" href="#item-method-isactive" aria-hidden="true">#</a> item.isActive()</h3>
Check if the item is currently _active_. Only active items are considered to be part of the layout.
**Returns** — _boolean_
**Examples**
```javascript
var isActive = item.isActive();
```
<h3><a id="item-method-isvisible" href="#item-method-isvisible" aria-hidden="true">#</a> item.isVisible()</h3>
Check if the item is currently _visible_.
**Returns** — _boolean_
**Examples**
```javascript
var isVisible = item.isVisible();
```
<h3><a id="item-method-isshowing" href="#item-method-isshowing" aria-hidden="true">#</a> item.isShowing()</h3>
Check if the item is currently animating to visible.
**Returns** — _boolean_
**Examples**
```javascript
var isShowing = item.isShowing();
```
<h3><a id="item-method-ishiding" href="#item-method-ishiding" aria-hidden="true">#</a> item.isHiding()</h3>
Check if the item is currently animating to hidden.
**Returns** — _boolean_
**Examples**
```javascript
var isHiding = item.isHiding();
```
<h3><a id="item-method-ispositioning" href="#item-method-ispositioning" aria-hidden="true">#</a> item.isPositioning()</h3>
Check if the item is currently being positioned.
**Returns** — _boolean_
**Examples**
```javascript
var isPositioning = item.isPositioning();
```
<h3><a id="item-method-isdragging" href="#item-method-isdragging" aria-hidden="true">#</a> item.isDragging()</h3>
Check if the item is currently being dragged.
**Returns** — _boolean_
**Examples**
```javascript
var isDragging = item.isDragging();
```
<h3><a id="item-method-isreleasing" href="#item-method-isreleasing" aria-hidden="true">#</a> item.isReleasing()</h3>
Check if the item is currently being released.
**Returns** — _boolean_
**Examples**
```javascript
var isReleasing = item.isReleasing();
```
<h3><a id="item-method-isdestroyed" href="#item-method-isdestroyed" aria-hidden="true">#</a> item.isDestroyed()</h3>
Check if the item is destroyed.
**Returns** — _boolean_
**Examples**
```javascript
var isDestroyed = item.isDestroyed();
```
<h2><a id="credits" href="#credits" aria-hidden="true">#</a> Credits</h2>
**Created and maintained by [Niklas Rämö](https://github.com/niklasramo).**
- This project owes much to David DeSandro's [Masonry](http://masonry.desandro.com/), [Packery](http://packery.metafizzy.co/) and [Isotope](https://isotope.metafizzy.co/) libraries. You should go ahead and check them out right now if you haven't yet. Thanks Dave!
- Jukka Jylänki's [survey](https://github.com/juj/RectangleBinPack) "A Thousand Ways to Pack the Bin - A Practical Approach to Two-Dimensional Rectangle Bin Packing" came in handy when building Muuri's layout algorithms. Thanks Jukka!
- Big thanks to the people behind [Web Animations polyfill](https://github.com/web-animations/web-animations-js) for making it possible to use Web Animations API reliably across browsers today.
- [Haltu Oy](http://www.haltu.fi/) was responsible for initiating this project in the first place and funded the initial development. Thanks Haltu!
<h2><a id="license" href="#license" aria-hidden="true">#</a> License</h2>
Copyright © 2015 Haltu Oy. Licensed under **[the MIT license](LICENSE.md)**.
================================================
FILE: dist/muuri.js
================================================
/**
* Muuri v0.9.5
* https://muuri.dev/
* Copyright (c) 2015-present, Haltu Oy
* Released under the MIT license
* https://github.com/haltu/muuri/blob/master/LICENSE.md
* @license MIT
*
* Muuri Packer
* Copyright (c) 2016-present, Niklas Rämö <inramo@gmail.com>
* @license MIT
*
* Muuri Ticker / Muuri Emitter / Muuri Dragger
* Copyright (c) 2018-present, Niklas Rämö <inramo@gmail.com>
* @license MIT
*
* Muuri AutoScroller
* Copyright (c) 2019-present, Niklas Rämö <inramo@gmail.com>
* @license MIT
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Muuri = factory());
}(this, (function () { 'use strict';
var GRID_INSTANCES = {};
var ITEM_ELEMENT_MAP = typeof Map === 'function' ? new Map() : null;
var ACTION_SWAP = 'swap';
var ACTION_MOVE = 'move';
var EVENT_SYNCHRONIZE = 'synchronize';
var EVENT_LAYOUT_START = 'layoutStart';
var EVENT_LAYOUT_END = 'layoutEnd';
var EVENT_LAYOUT_ABORT = 'layoutAbort';
var EVENT_ADD = 'add';
var EVENT_REMOVE = 'remove';
var EVENT_SHOW_START = 'showStart';
var EVENT_SHOW_END = 'showEnd';
var EVENT_HIDE_START = 'hideStart';
var EVENT_HIDE_END = 'hideEnd';
var EVENT_FILTER = 'filter';
var EVENT_SORT = 'sort';
var EVENT_MOVE = 'move';
var EVENT_SEND = 'send';
var EVENT_BEFORE_SEND = 'beforeSend';
var EVENT_RECEIVE = 'receive';
var EVENT_BEFORE_RECEIVE = 'beforeReceive';
var EVENT_DRAG_INIT = 'dragInit';
var EVENT_DRAG_START = 'dragStart';
var EVENT_DRAG_MOVE = 'dragMove';
var EVENT_DRAG_SCROLL = 'dragScroll';
var EVENT_DRAG_END = 'dragEnd';
var EVENT_DRAG_RELEASE_START = 'dragReleaseStart';
var EVENT_DRAG_RELEASE_END = 'dragReleaseEnd';
var EVENT_DESTROY = 'destroy';
var HAS_TOUCH_EVENTS = 'ontouchstart' in window;
var HAS_POINTER_EVENTS = !!window.PointerEvent;
var HAS_MS_POINTER_EVENTS = !!window.navigator.msPointerEnabled;
var MAX_SAFE_FLOAT32_INTEGER = 16777216;
/**
* Event emitter constructor.
*
* @class
*/
function Emitter() {
this._events = {};
this._queue = [];
this._counter = 0;
this._clearOnEmit = false;
}
/**
* Public prototype methods
* ************************
*/
/**
* Bind an event listener.
*
* @public
* @param {String} event
* @param {Function} listener
* @returns {Emitter}
*/
Emitter.prototype.on = function (event, listener) {
if (!this._events || !event || !listener) return this;
// Get listeners queue and create it if it does not exist.
var listeners = this._events[event];
if (!listeners) listeners = this._events[event] = [];
// Add the listener to the queue.
listeners.push(listener);
return this;
};
/**
* Unbind all event listeners that match the provided listener function.
*
* @public
* @param {String} event
* @param {Function} listener
* @returns {Emitter}
*/
Emitter.prototype.off = function (event, listener) {
if (!this._events || !event || !listener) return this;
// Get listeners and return immediately if none is found.
var listeners = this._events[event];
if (!listeners || !listeners.length) return this;
// Remove all matching listeners.
var index;
while ((index = listeners.indexOf(listener)) !== -1) {
listeners.splice(index, 1);
}
return this;
};
/**
* Unbind all listeners of the provided event.
*
* @public
* @param {String} event
* @returns {Emitter}
*/
Emitter.prototype.clear = function (event) {
if (!this._events || !event) return this;
var listeners = this._events[event];
if (listeners) {
listeners.length = 0;
delete this._events[event];
}
return this;
};
/**
* Emit all listeners in a specified event with the provided arguments.
*
* @public
* @param {String} event
* @param {...*} [args]
* @returns {Emitter}
*/
Emitter.prototype.emit = function (event) {
if (!this._events || !event) {
this._clearOnEmit = false;
return this;
}
// Get event listeners and quit early if there's no listeners.
var listeners = this._events[event];
if (!listeners || !listeners.length) {
this._clearOnEmit = false;
return this;
}
var queue = this._queue;
var startIndex = queue.length;
var argsLength = arguments.length - 1;
var args;
// If we have more than 3 arguments let's put the arguments in an array and
// apply it to the listeners.
if (argsLength > 3) {
args = [];
args.push.apply(args, arguments);
args.shift();
}
// Add the current listeners to the callback queue before we process them.
// This is necessary to guarantee that all of the listeners are called in
// correct order even if new event listeners are removed/added during
// processing and/or events are emitted during processing.
queue.push.apply(queue, listeners);
// Reset the event's listeners if need be.
if (this._clearOnEmit) {
listeners.length = 0;
this._clearOnEmit = false;
}
// Increment queue counter. This is needed for the scenarios where emit is
// triggered while the queue is already processing. We need to keep track of
// how many "queue processors" there are active so that we can safely reset
// the queue in the end when the last queue processor is finished.
++this._counter;
// Process the queue (the specific part of it for this emit).
var i = startIndex;
var endIndex = queue.length;
for (; i < endIndex; i++) {
// prettier-ignore
argsLength === 0 ? queue[i]() :
argsLength === 1 ? queue[i](arguments[1]) :
argsLength === 2 ? queue[i](arguments[1], arguments[2]) :
argsLength === 3 ? queue[i](arguments[1], arguments[2], arguments[3]) :
queue[i].apply(null, args);
// Stop processing if the emitter is destroyed.
if (!this._events) return this;
}
// Decrement queue process counter.
--this._counter;
// Reset the queue if there are no more queue processes running.
if (!this._counter) queue.length = 0;
return this;
};
/**
* Emit all listeners in a specified event with the provided arguments and
* remove the event's listeners just before calling the them. This method allows
* the emitter to serve as a queue where all listeners are called only once.
*
* @public
* @param {String} event
* @param {...*} [args]
* @returns {Emitter}
*/
Emitter.prototype.burst = function () {
if (!this._events) return this;
this._clearOnEmit = true;
this.emit.apply(this, arguments);
return this;
};
/**
* Check how many listeners there are for a specific event.
*
* @public
* @param {String} event
* @returns {Boolean}
*/
Emitter.prototype.countListeners = function (event) {
if (!this._events) return 0;
var listeners = this._events[event];
return listeners ? listeners.length : 0;
};
/**
* Destroy emitter instance. Basically just removes all bound listeners.
*
* @public
* @returns {Emitter}
*/
Emitter.prototype.destroy = function () {
if (!this._events) return this;
this._queue.length = this._counter = 0;
this._events = null;
return this;
};
var pointerout = HAS_POINTER_EVENTS ? 'pointerout' : HAS_MS_POINTER_EVENTS ? 'MSPointerOut' : '';
var waitDuration = 100;
/**
* If you happen to use Edge or IE on a touch capable device there is a
* a specific case where pointercancel and pointerend events are never emitted,
* even though one them should always be emitted when you release your finger
* from the screen. The bug appears specifically when Muuri shifts the dragged
* element's position in the DOM after pointerdown event, IE and Edge don't like
* that behaviour and quite often forget to emit the pointerend/pointercancel
* event. But, they do emit pointerout event so we utilize that here.
* Specifically, if there has been no pointermove event within 100 milliseconds
* since the last pointerout event we force cancel the drag operation. This hack
* works surprisingly well 99% of the time. There is that 1% chance there still
* that dragged items get stuck but it is what it is.
*
* @class
* @param {Dragger} dragger
*/
function EdgeHack(dragger) {
if (!pointerout) return;
this._dragger = dragger;
this._timeout = null;
this._outEvent = null;
this._isActive = false;
this._addBehaviour = this._addBehaviour.bind(this);
this._removeBehaviour = this._removeBehaviour.bind(this);
this._onTimeout = this._onTimeout.bind(this);
this._resetData = this._resetData.bind(this);
this._onStart = this._onStart.bind(this);
this._onOut = this._onOut.bind(this);
this._dragger.on('start', this._onStart);
}
/**
* @private
*/
EdgeHack.prototype._addBehaviour = function () {
if (this._isActive) return;
this._isActive = true;
this._dragger.on('move', this._resetData);
this._dragger.on('cancel', this._removeBehaviour);
this._dragger.on('end', this._removeBehaviour);
window.addEventListener(pointerout, this._onOut);
};
/**
* @private
*/
EdgeHack.prototype._removeBehaviour = function () {
if (!this._isActive) return;
this._dragger.off('move', this._resetData);
this._dragger.off('cancel', this._removeBehaviour);
this._dragger.off('end', this._removeBehaviour);
window.removeEventListener(pointerout, this._onOut);
this._resetData();
this._isActive = false;
};
/**
* @private
*/
EdgeHack.prototype._resetData = function () {
window.clearTimeout(this._timeout);
this._timeout = null;
this._outEvent = null;
};
/**
* @private
* @param {(PointerEvent|TouchEvent|MouseEvent)} e
*/
EdgeHack.prototype._onStart = function (e) {
if (e.pointerType === 'mouse') return;
this._addBehaviour();
};
/**
* @private
* @param {(PointerEvent|TouchEvent|MouseEvent)} e
*/
EdgeHack.prototype._onOut = function (e) {
if (!this._dragger._getTrackedTouch(e)) return;
this._resetData();
this._outEvent = e;
this._timeout = window.setTimeout(this._onTimeout, waitDuration);
};
/**
* @private
*/
EdgeHack.prototype._onTimeout = function () {
var e = this._outEvent;
this._resetData();
if (this._dragger.isActive()) this._dragger._onCancel(e);
};
/**
* @public
*/
EdgeHack.prototype.destroy = function () {
if (!pointerout) return;
this._dragger.off('start', this._onStart);
this._removeBehaviour();
};
// Playing it safe here, test all potential prefixes capitalized and lowercase.
var vendorPrefixes = ['', 'webkit', 'moz', 'ms', 'o', 'Webkit', 'Moz', 'MS', 'O'];
var cache$2 = {};
/**
* Get prefixed CSS property name when given a non-prefixed CSS property name.
* Returns null if the property is not supported at all.
*
* @param {CSSStyleDeclaration} style
* @param {String} prop
* @returns {String}
*/
function getPrefixedPropName(style, prop) {
var prefixedProp = cache$2[prop] || '';
if (prefixedProp) return prefixedProp;
var camelProp = prop[0].toUpperCase() + prop.slice(1);
var i = 0;
while (i < vendorPrefixes.length) {
prefixedProp = vendorPrefixes[i] ? vendorPrefixes[i] + camelProp : prop;
if (prefixedProp in style) {
cache$2[prop] = prefixedProp;
return prefixedProp;
}
++i;
}
return '';
}
/**
* Check if passive events are supported.
* https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
*
* @returns {Boolean}
*/
function hasPassiveEvents() {
var isPassiveEventsSupported = false;
try {
var passiveOpts = Object.defineProperty({}, 'passive', {
get: function () {
isPassiveEventsSupported = true;
},
});
window.addEventListener('testPassive', null, passiveOpts);
window.removeEventListener('testPassive', null, passiveOpts);
} catch (e) {}
return isPassiveEventsSupported;
}
var ua = window.navigator.userAgent.toLowerCase();
var isEdge = ua.indexOf('edge') > -1;
var isIE = ua.indexOf('trident') > -1;
var isFirefox = ua.indexOf('firefox') > -1;
var isAndroid = ua.indexOf('android') > -1;
var listenerOptions = hasPassiveEvents() ? { passive: true } : false;
var taProp = 'touchAction';
var taPropPrefixed = getPrefixedPropName(document.documentElement.style, taProp);
var taDefaultValue = 'auto';
/**
* Creates a new Dragger instance for an element.
*
* @public
* @class
* @param {HTMLElement} element
* @param {Object} [cssProps]
*/
function Dragger(element, cssProps) {
this._element = element;
this._emitter = new Emitter();
this._isDestroyed = false;
this._cssProps = {};
this._touchAction = '';
this._isActive = false;
this._pointerId = null;
this._startTime = 0;
this._startX = 0;
this._startY = 0;
this._currentX = 0;
this._currentY = 0;
this._onStart = this._onStart.bind(this);
this._onMove = this._onMove.bind(this);
this._onCancel = this._onCancel.bind(this);
this._onEnd = this._onEnd.bind(this);
// Can't believe had to build a freaking class for a hack!
this._edgeHack = null;
if ((isEdge || isIE) && (HAS_POINTER_EVENTS || HAS_MS_POINTER_EVENTS)) {
this._edgeHack = new EdgeHack(this);
}
// Apply initial CSS props.
this.setCssProps(cssProps);
// If touch action was not provided with initial CSS props let's assume it's
// auto.
if (!this._touchAction) {
this.setTouchAction(taDefaultValue);
}
// Prevent native link/image dragging for the item and it's children.
element.addEventListener('dragstart', Dragger._preventDefault, false);
// Listen to start event.
element.addEventListener(Dragger._inputEvents.start, this._onStart, listenerOptions);
}
/**
* Protected properties
* ********************
*/
Dragger._pointerEvents = {
start: 'pointerdown',
move: 'pointermove',
cancel: 'pointercancel',
end: 'pointerup',
};
Dragger._msPointerEvents = {
start: 'MSPointerDown',
move: 'MSPointerMove',
cancel: 'MSPointerCancel',
end: 'MSPointerUp',
};
Dragger._touchEvents = {
start: 'touchstart',
move: 'touchmove',
cancel: 'touchcancel',
end: 'touchend',
};
Dragger._mouseEvents = {
start: 'mousedown',
move: 'mousemove',
cancel: '',
end: 'mouseup',
};
Dragger._inputEvents = (function () {
if (HAS_TOUCH_EVENTS) return Dragger._touchEvents;
if (HAS_POINTER_EVENTS) return Dragger._pointerEvents;
if (HAS_MS_POINTER_EVENTS) return Dragger._msPointerEvents;
return Dragger._mouseEvents;
})();
Dragger._emitter = new Emitter();
Dragger._emitterEvents = {
start: 'start',
move: 'move',
end: 'end',
cancel: 'cancel',
};
Dragger._activeInstances = [];
/**
* Protected static methods
* ************************
*/
Dragger._preventDefault = function (e) {
if (e.preventDefault && e.cancelable !== false) e.preventDefault();
};
Dragger._activateInstance = function (instance) {
var index = Dragger._activeInstances.indexOf(instance);
if (index > -1) return;
Dragger._activeInstances.push(instance);
Dragger._emitter.on(Dragger._emitterEvents.move, instance._onMove);
Dragger._emitter.on(Dragger._emitterEvents.cancel, instance._onCancel);
Dragger._emitter.on(Dragger._emitterEvents.end, instance._onEnd);
if (Dragger._activeInstances.length === 1) {
Dragger._bindListeners();
}
};
Dragger._deactivateInstance = function (instance) {
var index = Dragger._activeInstances.indexOf(instance);
if (index === -1) return;
Dragger._activeInstances.splice(index, 1);
Dragger._emitter.off(Dragger._emitterEvents.move, instance._onMove);
Dragger._emitter.off(Dragger._emitterEvents.cancel, instance._onCancel);
Dragger._emitter.off(Dragger._emitterEvents.end, instance._onEnd);
if (!Dragger._activeInstances.length) {
Dragger._unbindListeners();
}
};
Dragger._bindListeners = function () {
window.addEventListener(Dragger._inputEvents.move, Dragger._onMove, listenerOptions);
window.addEventListener(Dragger._inputEvents.end, Dragger._onEnd, listenerOptions);
if (Dragger._inputEvents.cancel) {
window.addEventListener(Dragger._inputEvents.cancel, Dragger._onCancel, listenerOptions);
}
};
Dragger._unbindListeners = function () {
window.removeEventListener(Dragger._inputEvents.move, Dragger._onMove, listenerOptions);
window.removeEventListener(Dragger._inputEvents.end, Dragger._onEnd, listenerOptions);
if (Dragger._inputEvents.cancel) {
window.removeEventListener(Dragger._inputEvents.cancel, Dragger._onCancel, listenerOptions);
}
};
Dragger._getEventPointerId = function (event) {
// If we have pointer id available let's use it.
if (typeof event.pointerId === 'number') {
return event.pointerId;
}
// For touch events let's get the first changed touch's identifier.
if (event.changedTouches) {
return event.changedTouches[0] ? event.changedTouches[0].identifier : null;
}
// For mouse/other events let's provide a static id.
return 1;
};
Dragger._getTouchById = function (event, id) {
// If we have a pointer event return the whole event if there's a match, and
// null otherwise.
if (typeof event.pointerId === 'number') {
return event.pointerId === id ? event : null;
}
// For touch events let's check if there's a changed touch object that matches
// the pointerId in which case return the touch object.
if (event.changedTouches) {
for (var i = 0; i < event.changedTouches.length; i++) {
if (event.changedTouches[i].identifier === id) {
return event.changedTouches[i];
}
}
return null;
}
// For mouse/other events let's assume there's only one pointer and just
// return the event.
return event;
};
Dragger._onMove = function (e) {
Dragger._emitter.emit(Dragger._emitterEvents.move, e);
};
Dragger._onCancel = function (e) {
Dragger._emitter.emit(Dragger._emitterEvents.cancel, e);
};
Dragger._onEnd = function (e) {
Dragger._emitter.emit(Dragger._emitterEvents.end, e);
};
/**
* Private prototype methods
* *************************
*/
/**
* Reset current drag operation (if any).
*
* @private
*/
Dragger.prototype._reset = function () {
this._pointerId = null;
this._startTime = 0;
this._startX = 0;
this._startY = 0;
this._currentX = 0;
this._currentY = 0;
this._isActive = false;
Dragger._deactivateInstance(this);
};
/**
* Create a custom dragger event from a raw event.
*
* @private
* @param {String} type
* @param {(PointerEvent|TouchEvent|MouseEvent)} e
* @returns {Object}
*/
Dragger.prototype._createEvent = function (type, e) {
var touch = this._getTrackedTouch(e);
return {
// Hammer.js compatibility interface.
type: type,
srcEvent: e,
distance: this.getDistance(),
deltaX: this.getDeltaX(),
deltaY: this.getDeltaY(),
deltaTime: type === Dragger._emitterEvents.start ? 0 : this.getDeltaTime(),
isFirst: type === Dragger._emitterEvents.start,
isFinal: type === Dragger._emitterEvents.end || type === Dragger._emitterEvents.cancel,
pointerType: e.pointerType || (e.touches ? 'touch' : 'mouse'),
// Partial Touch API interface.
identifier: this._pointerId,
screenX: touch.screenX,
screenY: touch.screenY,
clientX: touch.clientX,
clientY: touch.clientY,
pageX: touch.pageX,
pageY: touch.pageY,
target: touch.target,
};
};
/**
* Emit a raw event as dragger event internally.
*
* @private
* @param {String} type
* @param {(PointerEvent|TouchEvent|MouseEvent)} e
*/
Dragger.prototype._emit = function (type, e) {
this._emitter.emit(type, this._createEvent(type, e));
};
/**
* If the provided event is a PointerEvent this method will return it if it has
* the same pointerId as the instance. If the provided event is a TouchEvent
* this method will try to look for a Touch instance in the changedTouches that
* has an identifier matching this instance's pointerId. If the provided event
* is a MouseEvent (or just any other event than PointerEvent or TouchEvent)
* it will be returned immediately.
*
* @private
* @param {(PointerEvent|TouchEvent|MouseEvent)} e
* @returns {?(Touch|PointerEvent|MouseEvent)}
*/
Dragger.prototype._getTrackedTouch = function (e) {
if (this._pointerId === null) return null;
return Dragger._getTouchById(e, this._pointerId);
};
/**
* Handler for start event.
*
* @private
* @param {(PointerEvent|TouchEvent|MouseEvent)} e
*/
Dragger.prototype._onStart = function (e) {
if (this._isDestroyed) return;
// If pointer id is already assigned let's return early.
if (this._pointerId !== null) return;
// Get (and set) pointer id.
this._pointerId = Dragger._getEventPointerId(e);
if (this._pointerId === null) return;
// Setup initial data and emit start event.
var touch = this._getTrackedTouch(e);
this._startX = this._currentX = touch.clientX;
this._startY = this._currentY = touch.clientY;
this._startTime = Date.now();
this._isActive = true;
this._emit(Dragger._emitterEvents.start, e);
// If the drag procedure was not reset within the start procedure let's
// activate the instance (start listening to move/cancel/end events).
if (this._isActive) {
Dragger._activateInstance(this);
}
};
/**
* Handler for move event.
*
* @private
* @param {(PointerEvent|TouchEvent|MouseEvent)} e
*/
Dragger.prototype._onMove = function (e) {
var touch = this._getTrackedTouch(e);
if (!touch) return;
this._currentX = touch.clientX;
this._currentY = touch.clientY;
this._emit(Dragger._emitterEvents.move, e);
};
/**
* Handler for cancel event.
*
* @private
* @param {(PointerEvent|TouchEvent|MouseEvent)} e
*/
Dragger.prototype._onCancel = function (e) {
if (!this._getTrackedTouch(e)) return;
this._emit(Dragger._emitterEvents.cancel, e);
this._reset();
};
/**
* Handler for end event.
*
* @private
* @param {(PointerEvent|TouchEvent|MouseEvent)} e
*/
Dragger.prototype._onEnd = function (e) {
if (!this._getTrackedTouch(e)) return;
this._emit(Dragger._emitterEvents.end, e);
this._reset();
};
/**
* Public prototype methods
* ************************
*/
/**
* Check if the element is being dragged at the moment.
*
* @public
* @returns {Boolean}
*/
Dragger.prototype.isActive = function () {
return this._isActive;
};
/**
* Set element's touch-action CSS property.
*
* @public
* @param {String} value
*/
Dragger.prototype.setTouchAction = function (value) {
// Store unmodified touch action value (we trust user input here).
this._touchAction = value;
// Set touch-action style.
if (taPropPrefixed) {
this._cssProps[taPropPrefixed] = '';
this._element.style[taPropPrefixed] = value;
}
// If we have an unsupported touch-action value let's add a special listener
// that prevents default action on touch start event. A dirty hack, but best
// we can do for now. The other options would be to somehow polyfill the
// unsupported touch action behavior with custom heuristics which sounds like
// a can of worms. We do a special exception here for Firefox Android which's
// touch-action does not work properly if the dragged element is moved in the
// the DOM tree on touchstart.
if (HAS_TOUCH_EVENTS) {
this._element.removeEventListener(Dragger._touchEvents.start, Dragger._preventDefault, true);
if (this._element.style[taPropPrefixed] !== value || (isFirefox && isAndroid)) {
this._element.addEventListener(Dragger._touchEvents.start, Dragger._preventDefault, true);
}
}
};
/**
* Update element's CSS properties. Accepts an object with camel cased style
* props with value pairs as it's first argument.
*
* @public
* @param {Object} [newProps]
*/
Dragger.prototype.setCssProps = function (newProps) {
if (!newProps) return;
var currentProps = this._cssProps;
var element = this._element;
var prop;
var prefixedProp;
// Reset current props.
for (prop in currentProps) {
element.style[prop] = currentProps[prop];
delete currentProps[prop];
}
// Set new props.
for (prop in newProps) {
// Make sure we have a value for the prop.
if (!newProps[prop]) continue;
// Special handling for touch-action.
if (prop === taProp) {
this.setTouchAction(newProps[prop]);
continue;
}
// Get prefixed prop and skip if it does not exist.
prefixedProp = getPrefixedPropName(element.style, prop);
if (!prefixedProp) continue;
// Store the prop and add the style.
currentProps[prefixedProp] = '';
element.style[prefixedProp] = newProps[prop];
}
};
/**
* How much the pointer has moved on x-axis from start position, in pixels.
* Positive value indicates movement from left to right.
*
* @public
* @returns {Number}
*/
Dragger.prototype.getDeltaX = function () {
return this._currentX - this._startX;
};
/**
* How much the pointer has moved on y-axis from start position, in pixels.
* Positive value indicates movement from top to bottom.
*
* @public
* @returns {Number}
*/
Dragger.prototype.getDeltaY = function () {
return this._currentY - this._startY;
};
/**
* How far (in pixels) has pointer moved from start position.
*
* @public
* @returns {Number}
*/
Dragger.prototype.getDistance = function () {
var x = this.getDeltaX();
var y = this.getDeltaY();
return Math.sqrt(x * x + y * y);
};
/**
* How long has pointer been dragged.
*
* @public
* @returns {Number}
*/
Dragger.prototype.getDeltaTime = function () {
return this._startTime ? Date.now() - this._startTime : 0;
};
/**
* Bind drag event listeners.
*
* @public
* @param {String} eventName
* - 'start', 'move', 'cancel' or 'end'.
* @param {Function} listener
*/
Dragger.prototype.on = function (eventName, listener) {
this._emitter.on(eventName, listener);
};
/**
* Unbind drag event listeners.
*
* @public
* @param {String} eventName
* - 'start', 'move', 'cancel' or 'end'.
* @param {Function} listener
*/
Dragger.prototype.off = function (eventName, listener) {
this._emitter.off(eventName, listener);
};
/**
* Destroy the instance and unbind all drag event listeners.
*
* @public
*/
Dragger.prototype.destroy = function () {
if (this._isDestroyed) return;
var element = this._element;
if (this._edgeHack) this._edgeHack.destroy();
// Reset data and deactivate the instance.
this._reset();
// Destroy emitter.
this._emitter.destroy();
// Unbind event handlers.
element.removeEventListener(Dragger._inputEvents.start, this._onStart, listenerOptions);
element.removeEventListener('dragstart', Dragger._preventDefault, false);
element.removeEventListener(Dragger._touchEvents.start, Dragger._preventDefault, true);
// Reset styles.
for (var prop in this._cssProps) {
element.style[prop] = this._cssProps[prop];
delete this._cssProps[prop];
}
// Reset data.
this._element = null;
// Mark as destroyed.
this._isDestroyed = true;
};
var dt = 1000 / 60;
var raf = (
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback) {
return this.setTimeout(function () {
callback(Date.now());
}, dt);
}
).bind(window);
/**
* A ticker system for handling DOM reads and writes in an efficient way.
*
* @class
*/
function Ticker(numLanes) {
this._nextStep = null;
this._lanes = [];
this._stepQueue = [];
this._stepCallbacks = {};
this._step = this._step.bind(this);
for (var i = 0; i < numLanes; i++) {
this._lanes.push(new TickerLane());
}
}
Ticker.prototype._step = function (time) {
var lanes = this._lanes;
var stepQueue = this._stepQueue;
var stepCallbacks = this._stepCallbacks;
var i, j, id, laneQueue, laneCallbacks, laneIndices;
this._nextStep = null;
for (i = 0; i < lanes.length; i++) {
laneQueue = lanes[i].queue;
laneCallbacks = lanes[i].callbacks;
laneIndices = lanes[i].indices;
for (j = 0; j < laneQueue.length; j++) {
id = laneQueue[j];
if (!id) continue;
stepQueue.push(id);
stepCallbacks[id] = laneCallbacks[id];
delete laneCallbacks[id];
delete laneIndices[id];
}
laneQueue.length = 0;
}
for (i = 0; i < stepQueue.length; i++) {
id = stepQueue[i];
if (stepCallbacks[id]) stepCallbacks[id](time);
delete stepCallbacks[id];
}
stepQueue.length = 0;
};
Ticker.prototype.add = function (laneIndex, id, callback) {
this._lanes[laneIndex].add(id, callback);
if (!this._nextStep) this._nextStep = raf(this._step);
};
Ticker.prototype.remove = function (laneIndex, id) {
this._lanes[laneIndex].remove(id);
};
/**
* A lane for ticker.
*
* @class
*/
function TickerLane() {
this.queue = [];
this.indices = {};
this.callbacks = {};
}
TickerLane.prototype.add = function (id, callback) {
var index = this.indices[id];
if (index !== undefined) this.queue[index] = undefined;
this.queue.push(id);
this.callbacks[id] = callback;
this.indices[id] = this.queue.length - 1;
};
TickerLane.prototype.remove = function (id) {
var index = this.indices[id];
if (index === undefined) return;
this.queue[index] = undefined;
delete this.callbacks[id];
delete this.indices[id];
};
var LAYOUT_READ = 'layoutRead';
var LAYOUT_WRITE = 'layoutWrite';
var VISIBILITY_READ = 'visibilityRead';
var VISIBILITY_WRITE = 'visibilityWrite';
var DRAG_START_READ = 'dragStartRead';
var DRAG_START_WRITE = 'dragStartWrite';
var DRAG_MOVE_READ = 'dragMoveRead';
var DRAG_MOVE_WRITE = 'dragMoveWrite';
var DRAG_SCROLL_READ = 'dragScrollRead';
var DRAG_SCROLL_WRITE = 'dragScrollWrite';
var DRAG_SORT_READ = 'dragSortRead';
var PLACEHOLDER_LAYOUT_READ = 'placeholderLayoutRead';
var PLACEHOLDER_LAYOUT_WRITE = 'placeholderLayoutWrite';
var PLACEHOLDER_RESIZE_WRITE = 'placeholderResizeWrite';
var AUTO_SCROLL_READ = 'autoScrollRead';
var AUTO_SCROLL_WRITE = 'autoScrollWrite';
var DEBOUNCE_READ = 'debounceRead';
var LANE_READ = 0;
var LANE_READ_TAIL = 1;
var LANE_WRITE = 2;
var ticker = new Ticker(3);
function addLayoutTick(itemId, read, write) {
ticker.add(LANE_READ, LAYOUT_READ + itemId, read);
ticker.add(LANE_WRITE, LAYOUT_WRITE + itemId, write);
}
function cancelLayoutTick(itemId) {
ticker.remove(LANE_READ, LAYOUT_READ + itemId);
ticker.remove(LANE_WRITE, LAYOUT_WRITE + itemId);
}
function addVisibilityTick(itemId, read, write) {
ticker.add(LANE_READ, VISIBILITY_READ + itemId, read);
ticker.add(LANE_WRITE, VISIBILITY_WRITE + itemId, write);
}
function cancelVisibilityTick(itemId) {
ticker.remove(LANE_READ, VISIBILITY_READ + itemId);
ticker.remove(LANE_WRITE, VISIBILITY_WRITE + itemId);
}
function addDragStartTick(itemId, read, write) {
ticker.add(LANE_READ, DRAG_START_READ + itemId, read);
ticker.add(LANE_WRITE, DRAG_START_WRITE + itemId, write);
}
function cancelDragStartTick(itemId) {
ticker.remove(LANE_READ, DRAG_START_READ + itemId);
ticker.remove(LANE_WRITE, DRAG_START_WRITE + itemId);
}
function addDragMoveTick(itemId, read, write) {
ticker.add(LANE_READ, DRAG_MOVE_READ + itemId, read);
ticker.add(LANE_WRITE, DRAG_MOVE_WRITE + itemId, write);
}
function cancelDragMoveTick(itemId) {
ticker.remove(LANE_READ, DRAG_MOVE_READ + itemId);
ticker.remove(LANE_WRITE, DRAG_MOVE_WRITE + itemId);
}
function addDragScrollTick(itemId, read, write) {
ticker.add(LANE_READ, DRAG_SCROLL_READ + itemId, read);
ticker.add(LANE_WRITE, DRAG_SCROLL_WRITE + itemId, write);
}
function cancelDragScrollTick(itemId) {
ticker.remove(LANE_READ, DRAG_SCROLL_READ + itemId);
ticker.remove(LANE_WRITE, DRAG_SCROLL_WRITE + itemId);
}
function addDragSortTick(itemId, read) {
ticker.add(LANE_READ_TAIL, DRAG_SORT_READ + itemId, read);
}
function cancelDragSortTick(itemId) {
ticker.remove(LANE_READ_TAIL, DRAG_SORT_READ + itemId);
}
function addPlaceholderLayoutTick(itemId, read, write) {
ticker.add(LANE_READ, PLACEHOLDER_LAYOUT_READ + itemId, read);
ticker.add(LANE_WRITE, PLACEHOLDER_LAYOUT_WRITE + itemId, write);
}
function cancelPlaceholderLayoutTick(itemId) {
ticker.remove(LANE_READ, PLACEHOLDER_LAYOUT_READ + itemId);
ticker.remove(LANE_WRITE, PLACEHOLDER_LAYOUT_WRITE + itemId);
}
function addPlaceholderResizeTick(itemId, write) {
ticker.add(LANE_WRITE, PLACEHOLDER_RESIZE_WRITE + itemId, write);
}
function cancelPlaceholderResizeTick(itemId) {
ticker.remove(LANE_WRITE, PLACEHOLDER_RESIZE_WRITE + itemId);
}
function addAutoScrollTick(read, write) {
ticker.add(LANE_READ, AUTO_SCROLL_READ, read);
ticker.add(LANE_WRITE, AUTO_SCROLL_WRITE, write);
}
function cancelAutoScrollTick() {
ticker.remove(LANE_READ, AUTO_SCROLL_READ);
ticker.remove(LANE_WRITE, AUTO_SCROLL_WRITE);
}
function addDebounceTick(debounceId, read) {
ticker.add(LANE_READ, DEBOUNCE_READ + debounceId, read);
}
function cancelDebounceTick(debounceId) {
ticker.remove(LANE_READ, DEBOUNCE_READ + debounceId);
}
var AXIS_X = 1;
var AXIS_Y = 2;
var FORWARD = 4;
var BACKWARD = 8;
var LEFT = AXIS_X | BACKWARD;
var RIGHT = AXIS_X | FORWARD;
var UP = AXIS_Y | BACKWARD;
var DOWN = AXIS_Y | FORWARD;
var functionType = 'function';
/**
* Check if a value is a function.
*
* @param {*} val
* @returns {Boolean}
*/
function isFunction(val) {
return typeof val === functionType;
}
var cache$1 = typeof WeakMap === 'function' ? new WeakMap() : null;
/**
* Returns the computed value of an element's style property as a string.
*
* @param {HTMLElement} element
* @param {String} style
* @returns {String}
*/
function getStyle(element, style) {
var styles = cache$1 && cache$1.get(element);
if (!styles) {
styles = window.getComputedStyle(element, null);
if (cache$1) cache$1.set(element, styles);
}
return styles.getPropertyValue(style);
}
/**
* Returns the computed value of an element's style property transformed into
* a float value.
*
* @param {HTMLElement} el
* @param {String} style
* @returns {Number}
*/
function getStyleAsFloat(el, style) {
return parseFloat(getStyle(el, style)) || 0;
}
var DOC_ELEM = document.documentElement;
var BODY = document.body;
var THRESHOLD_DATA = { value: 0, offset: 0 };
/**
* @param {HTMLElement|Window} element
* @returns {HTMLElement|Window}
*/
function getScrollElement(element) {
if (element === window || element === DOC_ELEM || element === BODY) {
return window;
} else {
return element;
}
}
/**
* @param {HTMLElement|Window} element
* @returns {Number}
*/
function getScrollLeft(element) {
return element === window ? element.pageXOffset : element.scrollLeft;
}
/**
* @param {HTMLElement|Window} element
* @returns {Number}
*/
function getScrollTop(element) {
return element === window ? element.pageYOffset : element.scrollTop;
}
/**
* @param {HTMLElement|Window} element
* @returns {Number}
*/
function getScrollLeftMax(element) {
if (element === window) {
return DOC_ELEM.scrollWidth - DOC_ELEM.clientWidth;
} else {
return element.scrollWidth - element.clientWidth;
}
}
/**
* @param {HTMLElement|Window} element
* @returns {Number}
*/
function getScrollTopMax(element) {
if (element === window) {
return DOC_ELEM.scrollHeight - DOC_ELEM.clientHeight;
} else {
return element.scrollHeight - element.clientHeight;
}
}
/**
* Get window's or element's client rectangle data relative to the element's
* content dimensions (includes inner size + padding, excludes scrollbars,
* borders and margins).
*
* @param {HTMLElement|Window} element
* @returns {Rectangle}
*/
function getContentRect(element, result) {
result = result || {};
if (element === window) {
result.width = DOC_ELEM.clientWidth;
result.height = DOC_ELEM.clientHeight;
result.left = 0;
result.right = result.width;
result.top = 0;
result.bottom = result.height;
} else {
var bcr = element.getBoundingClientRect();
var borderLeft = element.clientLeft || getStyleAsFloat(element, 'border-left-width');
var borderTop = element.clientTop || getStyleAsFloat(element, 'border-top-width');
result.width = element.clientWidth;
result.height = element.clientHeight;
result.left = bcr.left + borderLeft;
result.right = result.left + result.width;
result.top = bcr.top + borderTop;
result.bottom = result.top + result.height;
}
return result;
}
/**
* @param {Item} item
* @returns {Object}
*/
function getItemAutoScrollSettings(item) {
return item._drag._getGrid()._settings.dragAutoScroll;
}
/**
* @param {Item} item
*/
function prepareItemScrollSync(item) {
if (!item._drag) return;
item._drag._prepareScroll();
}
/**
* @param {Item} item
*/
function applyItemScrollSync(item) {
if (!item._drag || !item._isActive) return;
var drag = item._drag;
drag._scrollDiffX = drag._scrollDiffY = 0;
item._setTranslate(drag._left, drag._top);
}
/**
* Compute threshold value and edge offset.
*
* @param {Number} threshold
* @param {Number} safeZone
* @param {Number} itemSize
* @param {Number} targetSize
* @returns {Object}
*/
function computeThreshold(threshold, safeZone, itemSize, targetSize) {
THRESHOLD_DATA.value = Math.min(targetSize / 2, threshold);
THRESHOLD_DATA.offset =
Math.max(0, itemSize + THRESHOLD_DATA.value * 2 + targetSize * safeZone - targetSize) / 2;
return THRESHOLD_DATA;
}
function ScrollRequest() {
this.reset();
}
ScrollRequest.prototype.reset = function () {
if (this.isActive) this.onStop();
this.item = null;
this.element = null;
this.isActive = false;
this.isEnding = false;
this.direction = null;
this.value = null;
this.maxValue = 0;
this.threshold = 0;
this.distance = 0;
this.speed = 0;
this.duration = 0;
this.action = null;
};
ScrollRequest.prototype.hasReachedEnd = function () {
return FORWARD & this.direction ? this.value >= this.maxValue : this.value <= 0;
};
ScrollRequest.prototype.computeCurrentScrollValue = function () {
if (this.value === null) {
return AXIS_X & this.direction ? getScrollLeft(this.element) : getScrollTop(this.element);
}
return Math.max(0, Math.min(this.value, this.maxValue));
};
ScrollRequest.prototype.computeNextScrollValue = function (deltaTime) {
var delta = this.speed * (deltaTime / 1000);
var nextValue = FORWARD & this.direction ? this.value + delta : this.value - delta;
return Math.max(0, Math.min(nextValue, this.maxValue));
};
ScrollRequest.prototype.computeSpeed = (function () {
var data = {
direction: null,
threshold: 0,
distance: 0,
value: 0,
maxValue: 0,
deltaTime: 0,
duration: 0,
isEnding: false,
};
return function (deltaTime) {
var item = this.item;
var speed = getItemAutoScrollSettings(item).speed;
if (isFunction(speed)) {
data.direction = this.direction;
data.threshold = this.threshold;
data.distance = this.distance;
data.value = this.value;
data.maxValue = this.maxValue;
data.duration = this.duration;
data.speed = this.speed;
data.deltaTime = deltaTime;
data.isEnding = this.isEnding;
return speed(item, this.element, data);
} else {
return speed;
}
};
})();
ScrollRequest.prototype.tick = function (deltaTime) {
if (!this.isActive) {
this.isActive = true;
this.onStart();
}
this.value = this.computeCurrentScrollValue();
this.speed = this.computeSpeed(deltaTime);
this.value = this.computeNextScrollValue(deltaTime);
this.duration += deltaTime;
return this.value;
};
ScrollRequest.prototype.onStart = function () {
var item = this.item;
var onStart = getItemAutoScrollSettings(item).onStart;
if (isFunction(onStart)) onStart(item, this.element, this.direction);
};
ScrollRequest.prototype.onStop = function () {
var item = this.item;
var onStop = getItemAutoScrollSettings(item).onStop;
if (isFunction(onStop)) onStop(item, this.element, this.direction);
// Manually nudge sort to happen. There's a good chance that the item is still
// after the scroll stops which means that the next sort will be triggered
// only after the item is moved or it's parent scrolled.
if (item._drag) item._drag.sort();
};
function ScrollAction() {
this.element = null;
this.requestX = null;
this.requestY = null;
this.scrollLeft = 0;
this.scrollTop = 0;
}
ScrollAction.prototype.reset = function () {
if (this.requestX) this.requestX.action = null;
if (this.requestY) this.requestY.action = null;
this.element = null;
this.requestX = null;
this.requestY = null;
this.scrollLeft = 0;
this.scrollTop = 0;
};
ScrollAction.prototype.addRequest = function (request) {
if (AXIS_X & request.direction) {
this.removeRequest(this.requestX);
this.requestX = request;
} else {
this.removeRequest(this.requestY);
this.requestY = request;
}
request.action = this;
};
ScrollAction.prototype.removeRequest = function (request) {
if (!request) return;
if (this.requestX === request) {
this.requestX = null;
request.action = null;
} else if (this.requestY === request) {
this.requestY = null;
request.action = null;
}
};
ScrollAction.prototype.computeScrollValues = function () {
this.scrollLeft = this.requestX ? this.requestX.value : getScrollLeft(this.element);
this.scrollTop = this.requestY ? this.requestY.value : getScrollTop(this.element);
};
ScrollAction.prototype.scroll = function () {
var element = this.element;
if (!element) return;
if (element.scrollTo) {
element.scrollTo(this.scrollLeft, this.scrollTop);
} else {
element.scrollLeft = this.scrollLeft;
element.scrollTop = this.scrollTop;
}
};
function Pool(createItem, releaseItem) {
this.pool = [];
this.createItem = createItem;
this.releaseItem = releaseItem;
}
Pool.prototype.pick = function () {
return this.pool.pop() || this.createItem();
};
Pool.prototype.release = function (item) {
this.releaseItem(item);
if (this.pool.indexOf(item) !== -1) return;
this.pool.push(item);
};
Pool.prototype.reset = function () {
this.pool.length = 0;
};
/**
* Check if two rectangles are overlapping.
*
* @param {Object} a
* @param {Object} b
* @returns {Number}
*/
function isOverlapping(a, b) {
return !(
a.left + a.width <= b.left ||
b.left + b.width <= a.left ||
a.top + a.height <= b.top ||
b.top + b.height <= a.top
);
}
/**
* Calculate intersection area between two rectangle.
*
* @param {Object} a
* @param {Object} b
* @returns {Number}
*/
function getIntersectionArea(a, b) {
if (!isOverlapping(a, b)) return 0;
var width = Math.min(a.left + a.width, b.left + b.width) - Math.max(a.left, b.left);
var height = Math.min(a.top + a.height, b.top + b.height) - Math.max(a.top, b.top);
return width * height;
}
/**
* Calculate how many percent the intersection area of two rectangles is from
* the maximum potential intersection area between the rectangles.
*
* @param {Object} a
* @param {Object} b
* @returns {Number}
*/
function getIntersectionScore(a, b) {
var area = getIntersectionArea(a, b);
if (!area) return 0;
var maxArea = Math.min(a.width, b.width) * Math.min(a.height, b.height);
return (area / maxArea) * 100;
}
var RECT_1 = {
width: 0,
height: 0,
left: 0,
right: 0,
top: 0,
bottom: 0,
};
var RECT_2 = {
width: 0,
height: 0,
left: 0,
right: 0,
top: 0,
bottom: 0,
};
function AutoScroller() {
this._isDestroyed = false;
this._isTicking = false;
this._tickTime = 0;
this._tickDeltaTime = 0;
this._items = [];
this._actions = [];
this._requests = {};
this._requests[AXIS_X] = {};
this._requests[AXIS_Y] = {};
this._requestOverlapCheck = {};
this._dragPositions = {};
this._dragDirections = {};
this._overlapCheckInterval = 150;
this._requestPool = new Pool(
function () {
return new ScrollRequest();
},
function (request) {
request.reset();
}
);
this._actionPool = new Pool(
function () {
return new ScrollAction();
},
function (action) {
action.reset();
}
);
this._readTick = this._readTick.bind(this);
this._writeTick = this._writeTick.bind(this);
}
AutoScroller.AXIS_X = AXIS_X;
AutoScroller.AXIS_Y = AXIS_Y;
AutoScroller.FORWARD = FORWARD;
AutoScroller.BACKWARD = BACKWARD;
AutoScroller.LEFT = LEFT;
AutoScroller.RIGHT = RIGHT;
AutoScroller.UP = UP;
AutoScroller.DOWN = DOWN;
AutoScroller.smoothSpeed = function (maxSpeed, acceleration, deceleration) {
return function (item, element, data) {
var targetSpeed = 0;
if (!data.isEnding) {
if (data.threshold > 0) {
var factor = data.threshold - Math.max(0, data.distance);
targetSpeed = (maxSpeed / data.threshold) * factor;
} else {
targetSpeed = maxSpeed;
}
}
var currentSpeed = data.speed;
var nextSpeed = targetSpeed;
if (currentSpeed === targetSpeed) {
return nextSpeed;
}
if (currentSpeed < targetSpeed) {
nextSpeed = currentSpeed + acceleration * (data.deltaTime / 1000);
return Math.min(targetSpeed, nextSpeed);
} else {
nextSpeed = currentSpeed - deceleration * (data.deltaTime / 1000);
return Math.max(targetSpeed, nextSpeed);
}
};
};
AutoScroller.pointerHandle = function (pointerSize) {
var rect = { left: 0, top: 0, width: 0, height: 0 };
var size = pointerSize || 1;
return function (item, x, y, w, h, pX, pY) {
rect.left = pX - size * 0.5;
rect.top = pY - size * 0.5;
rect.width = size;
rect.height = size;
return rect;
};
};
AutoScroller.prototype._readTick = function (time) {
if (this._isDestroyed) return;
if (time && this._tickTime) {
this._tickDeltaTime = time - this._tickTime;
this._tickTime = time;
this._updateRequests();
this._updateActions();
} else {
this._tickTime = time;
this._tickDeltaTime = 0;
}
};
AutoScroller.prototype._writeTick = function () {
if (this._isDestroyed) return;
this._applyActions();
addAutoScrollTick(this._readTick, this._writeTick);
};
AutoScroller.prototype._startTicking = function () {
this._isTicking = true;
addAutoScrollTick(this._readTick, this._writeTick);
};
AutoScroller.prototype._stopTicking = function () {
this._isTicking = false;
this._tickTime = 0;
this._tickDeltaTime = 0;
cancelAutoScrollTick();
};
AutoScroller.prototype._getItemHandleRect = function (item, handle, rect) {
var itemDrag = item._drag;
if (handle) {
var ev = itemDrag._dragMoveEvent || itemDrag._dragStartEvent;
var data = handle(
item,
itemDrag._clientX,
itemDrag._clientY,
item._width,
item._height,
ev.clientX,
ev.clientY
);
rect.left = data.left;
rect.top = data.top;
rect.width = data.width;
rect.height = data.height;
} else {
rect.left = itemDrag._clientX;
rect.top = itemDrag._clientY;
rect.width = item._width;
rect.height = item._height;
}
rect.right = rect.left + rect.width;
rect.bottom = rect.top + rect.height;
return rect;
};
AutoScroller.prototype._requestItemScroll = function (
item,
axis,
element,
direction,
threshold,
distance,
maxValue
) {
var reqMap = this._requests[axis];
var request = reqMap[item._id];
if (request) {
if (request.element !== element || request.direction !== direction) {
request.reset();
}
} else {
request = this._requestPool.pick();
}
request.item = item;
request.element = element;
request.direction = direction;
request.threshold = threshold;
request.distance = distance;
request.maxValue = maxValue;
reqMap[item._id] = request;
};
AutoScroller.prototype._cancelItemScroll = function (item, axis) {
var reqMap = this._requests[axis];
var request = reqMap[item._id];
if (!request) return;
if (request.action) request.action.removeRequest(request);
this._requestPool.release(request);
delete reqMap[item._id];
};
AutoScroller.prototype._checkItemOverlap = function (item, checkX, checkY) {
var settings = getItemAutoScrollSettings(item);
var targets = isFunction(settings.targets) ? settings.targets(item) : settings.targets;
var threshold = settings.threshold;
var safeZone = settings.safeZone;
if (!targets || !targets.length) {
checkX && this._cancelItemScroll(item, AXIS_X);
checkY && this._cancelItemScroll(item, AXIS_Y);
return;
}
var dragDirections = this._dragDirections[item._id];
var dragDirectionX = dragDirections[0];
var dragDirectionY = dragDirections[1];
if (!dragDirectionX && !dragDirectionY) {
checkX && this._cancelItemScroll(item, AXIS_X);
checkY && this._cancelItemScroll(item, AXIS_Y);
return;
}
var itemRect = this._getItemHandleRect(item, settings.handle, RECT_1);
var testRect = RECT_2;
var target = null;
var testElement = null;
var testAxisX = true;
var testAxisY = true;
var testScore = 0;
var testPriority = 0;
var testThreshold = null;
var testDirection = null;
var testDistance = 0;
var testMaxScrollX = 0;
var testMaxScrollY = 0;
var xElement = null;
var xPriority = -Infinity;
var xThreshold = 0;
var xScore = 0;
var xDirection = null;
var xDistance = 0;
var xMaxScroll = 0;
var yElement = null;
var yPriority = -Infinity;
var yThreshold = 0;
var yScore = 0;
var yDirection = null;
var yDistance = 0;
var yMaxScroll = 0;
for (var i = 0; i < targets.length; i++) {
target = targets[i];
testAxisX = checkX && dragDirectionX && target.axis !== AXIS_Y;
testAxisY = checkY && dragDirectionY && target.axis !== AXIS_X;
testPriority = target.priority || 0;
// Ignore this item if it's x-axis and y-axis priority is lower than
// the currently matching item's.
if ((!testAxisX || testPriority < xPriority) && (!testAxisY || testPriority < yPriority)) {
continue;
}
testElement = getScrollElement(target.element || target);
testMaxScrollX = testAxisX ? getScrollLeftMax(testElement) : -1;
testMaxScrollY = testAxisY ? getScrollTopMax(testElement) : -1;
// Ignore this item if there is no possibility to scroll.
if (!testMaxScrollX && !testMaxScrollY) continue;
testRect = getContentRect(testElement, testRect);
testScore = getIntersectionScore(itemRect, testRect);
// Ignore this item if it's not overlapping at all with the dragged item.
if (testScore <= 0) continue;
// Test x-axis.
if (
testAxisX &&
testPriority >= xPriority &&
testMaxScrollX > 0 &&
(testPriority > xPriority || testScore > xScore)
) {
testDirection = null;
testThreshold = computeThreshold(
typeof target.threshold === 'number' ? target.threshold : threshold,
safeZone,
itemRect.width,
testRect.width
);
if (dragDirectionX === RIGHT) {
testDistance = testRect.right + testThreshold.offset - itemRect.right;
if (testDistance <= testThreshold.value && getScrollLeft(testElement) < testMaxScrollX) {
testDirection = RIGHT;
}
} else if (dragDirectionX === LEFT) {
testDistance = itemRect.left - (testRect.left - testThreshold.offset);
if (testDistance <= testThreshold.value && getScrollLeft(testElement) > 0) {
testDirection = LEFT;
}
}
if (testDirection !== null) {
xElement = testElement;
xPriority = testPriority;
xThreshold = testThreshold.value;
xScore = testScore;
xDirection = testDirection;
xDistance = testDistance;
xMaxScroll = testMaxScrollX;
}
}
// Test y-axis.
if (
testAxisY &&
testPriority >= yPriority &&
testMaxScrollY > 0 &&
(testPriority > yPriority || testScore > yScore)
) {
testDirection = null;
testThreshold = computeThreshold(
typeof target.threshold === 'number' ? target.threshold : threshold,
safeZone,
itemRect.height,
testRect.height
);
if (dragDirectionY === DOWN) {
testDistance = testRect.bottom + testThreshold.offset - itemRect.bottom;
if (testDistance <= testThreshold.value && getScrollTop(testElement) < testMaxScrollY) {
testDirection = DOWN;
}
} else if (dragDirectionY === UP) {
testDistance = itemRect.top - (testRect.top - testThreshold.offset);
if (testDistance <= testThreshold.value && getScrollTop(testElement) > 0) {
testDirection = UP;
}
}
if (testDirection !== null) {
yElement = testElement;
yPriority = testPriority;
yThreshold = testThreshold.value;
yScore = testScore;
yDirection = testDirection;
yDistance = testDistance;
yMaxScroll = testMaxScrollY;
}
}
}
// Request or cancel x-axis scroll.
if (checkX) {
if (xElement) {
this._requestItemScroll(
item,
AXIS_X,
xElement,
xDirection,
xThreshold,
xDistance,
xMaxScroll
);
} else {
this._cancelItemScroll(item, AXIS_X);
}
}
// Request or cancel y-axis scroll.
if (checkY) {
if (yElement) {
this._requestItemScroll(
item,
AXIS_Y,
yElement,
yDirection,
yThreshold,
yDistance,
yMaxScroll
);
} else {
this._cancelItemScroll(item, AXIS_Y);
}
}
};
AutoScroller.prototype._updateScrollRequest = function (scrollRequest) {
var item = scrollRequest.item;
var settings = getItemAutoScrollSettings(item);
var targets = isFunction(settings.targets) ? settings.targets(item) : settings.targets;
var targetCount = (targets && targets.length) || 0;
var threshold = settings.threshold;
var safeZone = settings.safeZone;
var itemRect = this._getItemHandleRect(item, settings.handle, RECT_1);
var testRect = RECT_2;
var target = null;
var testElement = null;
var testIsAxisX = false;
var testScore = null;
var testThreshold = null;
var testDistance = null;
var testScroll = null;
var testMaxScroll = null;
var hasReachedEnd = null;
for (var i = 0; i < targetCount; i++) {
target = targets[i];
// Make sure we have a matching element.
testElement = getScrollElement(target.element || target);
if (testElement !== scrollRequest.element) continue;
// Make sure we have a matching axis.
testIsAxisX = !!(AXIS_X & scrollRequest.direction);
if (testIsAxisX) {
if (target.axis === AXIS_Y) continue;
} else {
if (target.axis === AXIS_X) continue;
}
// Stop scrolling if there is no room to scroll anymore.
testMaxScroll = testIsAxisX ? getScrollLeftMax(testElement) : getScrollTopMax(testElement);
if (testMaxScroll <= 0) {
break;
}
testRect = getContentRect(testElement, testRect);
testScore = getIntersectionScore(itemRect, testRect);
// Stop scrolling if dragged item is not overlapping with the scroll
// element anymore.
if (testScore <= 0) {
break;
}
// Compute threshold and edge offset.
testThreshold = computeThreshold(
typeof target.threshold === 'number' ? target.threshold : threshold,
safeZone,
testIsAxisX ? itemRect.width : itemRect.height,
testIsAxisX ? testRect.width : testRect.height
);
// Compute distance (based on current direction).
if (scrollRequest.direction === LEFT) {
testDistance = itemRect.left - (testRect.left - testThreshold.offset);
} else if (scrollRequest.direction === RIGHT) {
testDistance = testRect.right + testThreshold.offset - itemRect.right;
} else if (scrollRequest.direction === UP) {
testDistance = itemRect.top - (testRect.top - testThreshold.offset);
} else {
testDistance = testRect.bottom + testThreshold.offset - itemRect.bottom;
}
// Stop scrolling if threshold is not exceeded.
if (testDistance > testThreshold.value) {
break;
}
// Stop scrolling if we have reached the end of the scroll value.
testScroll = testIsAxisX ? getScrollLeft(testElement) : getScrollTop(testElement);
hasReachedEnd =
FORWARD & scrollRequest.direction ? testScroll >= testMaxScroll : testScroll <= 0;
if (hasReachedEnd) {
break;
}
// Scrolling can continue, let's update the values.
scrollRequest.maxValue = testMaxScroll;
scrollRequest.threshold = testThreshold.value;
scrollRequest.distance = testDistance;
scrollRequest.isEnding = false;
return true;
}
// Before we end the request, let's see if we need to stop the scrolling
// smoothly or immediately.
if (settings.smoothStop === true && scrollRequest.speed > 0) {
if (hasReachedEnd === null) hasReachedEnd = scrollRequest.hasReachedEnd();
scrollRequest.isEnding = hasReachedEnd ? false : true;
} else {
scrollRequest.isEnding = false;
}
return scrollRequest.isEnding;
};
AutoScroller.prototype._updateRequests = function () {
var items = this._items;
var requestsX = this._requests[AXIS_X];
var requestsY = this._requests[AXIS_Y];
var item, reqX, reqY, checkTime, needsCheck, checkX, checkY;
for (var i = 0; i < items.length; i++) {
item = items[i];
checkTime = this._requestOverlapCheck[item._id];
needsCheck = checkTime > 0 && this._tickTime - checkTime > this._overlapCheckInterval;
checkX = true;
reqX = requestsX[item._id];
if (reqX && reqX.isActive) {
checkX = !this._updateScrollRequest(reqX);
if (checkX) {
needsCheck = true;
this._cancelItemScroll(item, AXIS_X);
}
}
checkY = true;
reqY = requestsY[item._id];
if (reqY && reqY.isActive) {
checkY = !this._updateScrollRequest(reqY);
if (checkY) {
needsCheck = true;
this._cancelItemScroll(item, AXIS_Y);
}
}
if (needsCheck) {
this._requestOverlapCheck[item._id] = 0;
this._checkItemOverlap(item, checkX, checkY);
}
}
};
AutoScroller.prototype._requestAction = function (request, axis) {
var actions = this._actions;
var isAxisX = axis === AXIS_X;
var action = null;
for (var i = 0; i < actions.length; i++) {
action = actions[i];
// If the action's request does not match the request's -> skip.
if (request.element !== action.element) {
action = null;
continue;
}
// If the request and action share the same element, but the request slot
// for the requested axis is already reserved let's ignore and cancel this
// request.
if (isAxisX ? action.requestX : action.requestY) {
this._cancelItemScroll(request.item, axis);
return;
}
// Seems like we have found our action, let's break the loop.
break;
}
if (!action) action = this._actionPool.pick();
action.element = request.element;
action.addRequest(request);
request.tick(this._tickDeltaTime);
actions.push(action);
};
AutoScroller.prototype._updateActions = function () {
var items = this._items;
var requests = this._requests;
var actions = this._actions;
var itemId;
var reqX;
var reqY;
var i;
// Generate actions.
for (i = 0; i < items.length; i++) {
itemId = items[i]._id;
reqX = requests[AXIS_X][itemId];
reqY = requests[AXIS_Y][itemId];
if (reqX) this._requestAction(reqX, AXIS_X);
if (reqY) this._requestAction(reqY, AXIS_Y);
}
// Compute actions' scroll values.
for (i = 0; i < actions.length; i++) {
actions[i].computeScrollValues();
}
};
AutoScroller.prototype._applyActions = function () {
var actions = this._actions;
var items = this._items;
var i;
// No actions -> no scrolling.
if (!actions.length) return;
// Scroll all the required elements.
for (i = 0; i < actions.length; i++) {
actions[i].scroll();
this._actionPool.release(actions[i]);
}
// Reset actions.
actions.length = 0;
// Sync the item position immediately after all the auto-scrolling business is
// finished. Without this procedure the items will jitter during auto-scroll
// (in some cases at least) since the drag scroll handler is async (bound to
// raf tick). Note that this procedure should not emit any dragScroll events,
// because otherwise they would be emitted twice for the same event.
for (i = 0; i < items.length; i++) prepareItemScrollSync(items[i]);
for (i = 0; i < items.length; i++) applyItemScrollSync(items[i]);
};
AutoScroller.prototype._updateDragDirection = function (item) {
var dragPositions = this._dragPositions[item._id];
var dragDirections = this._dragDirections[item._id];
var x1 = item._drag._left;
var y1 = item._drag._top;
if (dragPositions.length) {
var x2 = dragPositions[0];
var y2 = dragPositions[1];
dragDirections[0] = x1 > x2 ? RIGHT : x1 < x2 ? LEFT : dragDirections[0] || 0;
dragDirections[1] = y1 > y2 ? DOWN : y1 < y2 ? UP : dragDirections[1] || 0;
}
dragPositions[0] = x1;
dragPositions[1] = y1;
};
AutoScroller.prototype.addItem = function (item) {
if (this._isDestroyed) return;
var index = this._items.indexOf(item);
if (index === -1) {
this._items.push(item);
this._requestOverlapCheck[item._id] = this._tickTime;
this._dragDirections[item._id] = [0, 0];
this._dragPositions[item._id] = [];
if (!this._isTicking) this._startTicking();
}
};
AutoScroller.prototype.updateItem = function (item) {
if (this._isDestroyed) return;
// Make sure the item still exists in the auto-scroller.
if (!this._dragDirections[item._id]) return;
this._updateDragDirection(item);
if (!this._requestOverlapCheck[item._id]) {
this._requestOverlapCheck[item._id] = this._tickTime;
}
};
AutoScroller.prototype.removeItem = function (item) {
if (this._isDestroyed) return;
var index = this._items.indexOf(item);
if (index === -1) return;
var itemId = item._id;
var reqX = this._requests[AXIS_X][itemId];
if (reqX) {
this._cancelItemScroll(item, AXIS_X);
delete this._requests[AXIS_X][itemId];
}
var reqY = this._requests[AXIS_Y][itemId];
if (reqY) {
this._cancelItemScroll(item, AXIS_Y);
delete this._requests[AXIS_Y][itemId];
}
delete this._requestOverlapCheck[itemId];
delete this._dragPositions[itemId];
delete this._dragDirections[itemId];
this._items.splice(index, 1);
if (this._isTicking && !this._items.length) {
this._stopTicking();
}
};
AutoScroller.prototype.isItemScrollingX = function (item) {
var reqX = this._requests[AXIS_X][item._id];
return !!(reqX && reqX.isActive);
};
AutoScroller.prototype.isItemScrollingY = function (item) {
var reqY = this._requests[AXIS_Y][item._id];
return !!(reqY && reqY.isActive);
};
AutoScroller.prototype.isItemScrolling = function (item) {
return this.isItemScrollingX(item) || this.isItemScrollingY(item);
};
AutoScroller.prototype.destroy = function () {
if (this._isDestroyed) return;
var items = this._items.slice(0);
for (var i = 0; i < items.length; i++) {
this.removeItem(items[i]);
}
this._actions.length = 0;
this._requestPool.reset();
this._actionPool.reset();
this._isDestroyed = true;
};
var ElProto = window.Element.prototype;
var matchesFn =
ElProto.matches ||
ElProto.matchesSelector ||
ElProto.webkitMatchesSelector ||
ElProto.mozMatchesSelector ||
ElProto.msMatchesSelector ||
ElProto.oMatchesSelector ||
function () {
return false;
};
/**
* Check if element matches a CSS selector.
*
* @param {Element} el
* @param {String} selector
* @returns {Boolean}
*/
function elementMatches(el, selector) {
return matchesFn.call(el, selector);
}
/**
* Add class to an element.
*
* @param {HTMLElement} element
* @param {String} className
*/
function addClass(element, className) {
if (!className) return;
if (element.classList) {
element.classList.add(className);
} else {
if (!elementMatches(element, '.' + className)) {
element.className += ' ' + className;
}
}
}
var tempArray = [];
var numberType = 'number';
/**
* Insert an item or an array of items to array to a specified index. Mutates
* the array. The index can be negative in which case the items will be added
* to the end of the array.
*
* @param {Array} array
* @param {*} items
* @param {Number} [index=-1]
*/
function arrayInsert(array, items, index) {
var startIndex = typeof index === numberType ? index : -1;
if (startIndex < 0) startIndex = array.length - startIndex + 1;
array.splice.apply(array, tempArray.concat(startIndex, 0, items));
tempArray.length = 0;
}
/**
* Normalize array index. Basically this function makes sure that the provided
* array index is within the bounds of the provided array and also transforms
* negative index to the matching positive index. The third (optional) argument
* allows you to define offset for array's length in case you are adding items
* to the array or removing items from the array.
*
* @param {Array} array
* @param {Number} index
* @param {Number} [sizeOffset]
*/
function normalizeArrayIndex(array, index, sizeOffset) {
var maxIndex = Math.max(0, array.length - 1 + (sizeOffset || 0));
return index > maxIndex ? maxIndex : index < 0 ? Math.max(maxIndex + index + 1, 0) : index;
}
/**
* Move array item to another index.
*
* @param {Array} array
* @param {Number} fromIndex
* - Index (positive or negative) of the item that will be moved.
* @param {Number} toIndex
* - Index (positive or negative) where the item should be moved to.
*/
function arrayMove(array, fromIndex, toIndex) {
// Make sure the array has two or more items.
if (array.length < 2) return;
// Normalize the indices.
var from = normalizeArrayIndex(array, fromIndex);
var to = normalizeArrayIndex(array, toIndex);
// Add target item to the new position.
if (from !== to) {
array.splice(to, 0, array.splice(from, 1)[0]);
}
}
/**
* Swap array items.
*
* @param {Array} array
* @param {Number} index
* - Index (positive or negative) of the item that will be swapped.
* @param {Number} withIndex
* - Index (positive or negative) of the other item that will be swapped.
*/
function arraySwap(array, index, withIndex) {
// Make sure the array has two or more items.
if (array.length < 2) return;
// Normalize the indices.
var indexA = normalizeArrayIndex(array, index);
var indexB = normalizeArrayIndex(array, withIndex);
var temp;
// Swap the items.
if (indexA !== indexB) {
temp = array[indexA];
array[indexA] = array[indexB];
array[indexB] = temp;
}
}
var transformProp = getPrefixedPropName(document.documentElement.style, 'transform') || 'transform';
var styleNameRegEx = /([A-Z])/g;
var prefixRegex = /^(webkit-|moz-|ms-|o-)/;
var msPrefixRegex = /^(-m-s-)/;
/**
* Transforms a camel case style property to kebab case style property. Handles
* vendor prefixed properties elegantly as well, e.g. "WebkitTransform" and
* "webkitTransform" are both transformed into "-webkit-transform".
*
* @param {String} property
* @returns {String}
*/
function getStyleName(property) {
// Initial slicing, turns "fooBarProp" into "foo-bar-prop".
var styleName = property.replace(styleNameRegEx, '-$1').toLowerCase();
// Handle properties that start with "webkit", "moz", "ms" or "o" prefix (we
// need to add an extra '-' to the beginnig).
styleName = styleName.replace(prefixRegex, '-$1');
// Handle properties that start with "MS" prefix (we need to transform the
// "-m-s-" into "-ms-").
styleName = styleName.replace(msPrefixRegex, '-ms-');
return styleName;
}
var transformStyle = getStyleName(transformProp);
var transformNone$1 = 'none';
var displayInline = 'inline';
var displayNone = 'none';
var displayStyle = 'display';
/**
* Returns true if element is transformed, false if not. In practice the
* element's display value must be anything else than "none" or "inline" as
* well as have a valid transform value applied in order to be counted as a
* transformed element.
*
* Borrowed from Mezr (v0.6.1):
* https://github.com/niklasramo/mezr/blob/0.6.1/mezr.js#L661
*
* @param {HTMLElement} element
* @returns {Boolean}
*/
function isTransformed(element) {
var transform = getStyle(element, transformStyle);
if (!transform || transform === transformNone$1) return false;
var display = getStyle(element, displayStyle);
if (display === displayInline || display === displayNone) return false;
return true;
}
/**
* Returns an absolute positioned element's containing block, which is
* considered to be the closest ancestor element that the target element's
* positioning is relative to. Disclaimer: this only works as intended for
* absolute positioned elements.
*
* @param {HTMLElement} element
* @returns {(Document|Element)}
*/
function getContainingBlock(element) {
// As long as the containing block is an element, static and not
// transformed, try to get the element's parent element and fallback to
// document. https://github.com/niklasramo/mezr/blob/0.6.1/mezr.js#L339
var doc = document;
var res = element || doc;
while (res && res !== doc && getStyle(res, 'position') === 'static' && !isTransformed(res)) {
res = res.parentElement || doc;
}
return res;
}
var offsetA = {};
var offsetB = {};
var offsetDiff = {};
/**
* Returns the element's document offset, which in practice means the vertical
* and horizontal distance between the element's northwest corner and the
* document's northwest corner. Note that this function always returns the same
* object so be sure to read the data from it instead using it as a reference.
*
* @param {(Document|Element|Window)} element
* @param {Object} [offsetData]
* - Optional data object where the offset data will be inserted to. If not
* provided a new object will be created for the return data.
* @returns {Object}
*/
function getOffset(element, offsetData) {
var offset = offsetData || {};
var rect;
// Set up return data.
offset.left = 0;
offset.top = 0;
// Document's offsets are always 0.
if (element === document) return offset;
// Add viewport scroll left/top to the respective offsets.
offset.left = window.pageXOffset || 0;
offset.top = window.pageYOffset || 0;
// Window's offsets are the viewport scroll left/top values.
if (element.self === window.self) return offset;
// Add element's client rects to the offsets.
rect = element.getBoundingClientRect();
offset.left += rect.left;
offset.top += rect.top;
// Exclude element's borders from the offset.
offset.left += getStyleAsFloat(element, 'border-left-width');
offset.top += getStyleAsFloat(element, 'border-top-width');
return offset;
}
/**
* Calculate the offset difference two elements.
*
* @param {HTMLElement} elemA
* @param {HTMLElement} elemB
* @param {Boolean} [compareContainingBlocks=false]
* - When this is set to true the containing blocks of the provided elements
* will be used for calculating the difference. Otherwise the provided
* elements will be compared directly.
* @returns {Object}
*/
function getOffsetDiff(elemA, elemB, compareContainingBlocks) {
offsetDiff.left = 0;
offsetDiff.top = 0;
// If elements are same let's return early.
if (elemA === elemB) return offsetDiff;
// Compare containing blocks if necessary.
if (compareContainingBlocks) {
elemA = getContainingBlock(elemA);
elemB = getContainingBlock(elemB);
// If containing blocks are identical, let's return early.
if (elemA === elemB) return offsetDiff;
}
// Finally, let's calculate the offset diff.
getOffset(elemA, offsetA);
getOffset(elemB, offsetB);
offsetDiff.left = offsetB.left - offsetA.left;
offsetDiff.top = offsetB.top - offsetA.top;
return offsetDiff;
}
/**
* Check if overflow style value is scrollable.
*
* @param {String} value
* @returns {Boolean}
*/
function isScrollableOverflow(value) {
return value === 'auto' || value === 'scroll' || value === 'overlay';
}
/**
* Check if an element is scrollable.
*
* @param {HTMLElement} element
* @returns {Boolean}
*/
function isScrollable(element) {
return (
isScrollableOverflow(getStyle(element, 'overflow')) ||
isScrollableOverflow(getStyle(element, 'overflow-x')) ||
isScrollableOverflow(getStyle(element, 'overflow-y'))
);
}
/**
* Collect element's ancestors that are potentially scrollable elements. The
* provided element is also also included in the check, meaning that if it is
* scrollable it is added to the result array.
*
* @param {HTMLElement} element
* @param {Array} [result]
* @returns {Array}
*/
function getScrollableAncestors(element, result) {
result = result || [];
// Find scroll parents.
while (element && element !== document) {
// If element is inside ShadowDOM let's get it's host node from the real
// DOM and continue looping.
if (element.getRootNode && element instanceof DocumentFragment) {
element = element.getRootNode().host;
continue;
}
// If element is scrollable let's add it to the scrollable list.
if (isScrollable(element)) {
result.push(element);
}
element = element.parentNode;
}
// Always add window to the results.
result.push(window);
return result;
}
var translateValue = {};
var transformNone = 'none';
var rxMat3d = /^matrix3d/;
var rxMatTx = /([^,]*,){4}/;
var rxMat3dTx = /([^,]*,){12}/;
var rxNextItem = /[^,]*,/;
/**
* Returns the element's computed translateX and translateY values as a floats.
* The returned object is always the same object and updated every time this
* function is called.
*
* @param {HTMLElement} element
* @returns {Object}
*/
function getTranslate(element) {
translateValue.x = 0;
translateValue.y = 0;
var transform = getStyle(element, transformStyle);
if (!transform || transform === transformNone) {
return translateValue;
}
// Transform style can be in either matrix3d(...) or matrix(...).
var isMat3d = rxMat3d.test(transform);
var tX = transform.replace(isMat3d ? rxMat3dTx : rxMatTx, '');
var tY = tX.replace(rxNextItem, '');
translateValue.x = parseFloat(tX) || 0;
translateValue.y = parseFloat(tY) || 0;
return translateValue;
}
/**
* Remove class from an element.
*
* @param {HTMLElement} element
* @param {String} className
*/
function removeClass(element, className) {
if (!className) return;
if (element.classList) {
element.classList.remove(className);
} else {
if (elementMatches(element, '.' + className)) {
element.className = (' ' + element.className + ' ')
.replace(' ' + className + ' ', ' ')
.trim();
}
}
}
var IS_IOS =
/^(iPad|iPhone|iPod)/.test(window.navigator.platform) ||
(/^Mac/.test(window.navigator.platform) && window.navigator.maxTouchPoints > 1);
var START_PREDICATE_INACTIVE = 0;
var START_PREDICATE_PENDING = 1;
var START_PREDICATE_RESOLVED = 2;
var SCROLL_LISTENER_OPTIONS = hasPassiveEvents() ? { passive: true } : false;
/**
* Bind touch interaction to an item.
*
* @class
* @param {Item} item
*/
function ItemDrag(item) {
var element = item._element;
var grid = item.getGrid();
var settings = grid._settings;
this._item = item;
this._gridId = grid._id;
this._isDestroyed = false;
this._isMigrating = false;
// Start predicate data.
this._startPredicate = isFunction(settings.dragStartPredicate)
? settings.dragStartPredicate
: ItemDrag.defaultStartPredicate;
this._startPredicateState = START_PREDICATE_INACTIVE;
this._startPredicateResult = undefined;
// Data for drag sort predicate heuristics.
this._isSortNeeded = false;
this._sortTimer = undefined;
this._blockedSortIndex = null;
this._sortX1 = 0;
this._sortX2 = 0;
this._sortY1 = 0;
this._sortY2 = 0;
// Setup item's initial drag data.
this._reset();
// Bind the methods that needs binding.
this._preStartCheck = this._preStartCheck.bind(this);
this._preEndCheck = this._preEndCheck.bind(this);
this._onScroll = this._onScroll.bind(this);
this._prepareStart = th
gitextract_btx3_qaq/
├── .eslintrc.js
├── .github/
│ └── FUNDING.yml
├── .gitignore
├── .prettierrc.json
├── .travis.yml
├── AUTHORS.txt
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── dist/
│ ├── muuri.js
│ └── muuri.module.js
├── docs/
│ ├── .vitepress/
│ │ ├── config.js
│ │ └── theme/
│ │ ├── custom.css
│ │ └── index.js
│ ├── examples.md
│ ├── getting-started.md
│ ├── grid-constructor.md
│ ├── grid-events.md
│ ├── grid-methods.md
│ ├── grid-options.md
│ ├── index.md
│ └── item-methods.md
├── gulpfile.js
├── karma.conf.js
├── karma.defaults.js
├── package.json
├── rollup.banner.js
├── rollup.config.js
├── src/
│ ├── Animator/
│ │ └── Animator.js
│ ├── AutoScroller/
│ │ ├── AutoScroller.js
│ │ ├── LICENSE.md
│ │ ├── Pool.js
│ │ ├── ScrollAction.js
│ │ ├── ScrollRequest.js
│ │ ├── constants.js
│ │ └── utils.js
│ ├── Dragger/
│ │ ├── Dragger.js
│ │ ├── EdgeHack.js
│ │ └── LICENSE.md
│ ├── Emitter/
│ │ ├── Emitter.js
│ │ └── LICENSE.md
│ ├── Grid/
│ │ └── Grid.js
│ ├── Item/
│ │ ├── Item.js
│ │ ├── ItemDrag.js
│ │ ├── ItemDragPlaceholder.js
│ │ ├── ItemDragRelease.js
│ │ ├── ItemLayout.js
│ │ ├── ItemMigrate.js
│ │ └── ItemVisibility.js
│ ├── Packer/
│ │ ├── LICENSE.md
│ │ ├── Packer.js
│ │ └── PackerProcessor.js
│ ├── Ticker/
│ │ ├── LICENSE.md
│ │ └── Ticker.js
│ ├── constants.js
│ ├── index.d.ts
│ ├── index.js
│ ├── ticker.js
│ └── utils/
│ ├── addClass.js
│ ├── arrayInsert.js
│ ├── arrayMove.js
│ ├── arraySwap.js
│ ├── createUid.js
│ ├── debounce.js
│ ├── elementMatches.js
│ ├── getContainingBlock.js
│ ├── getCurrentStyles.js
│ ├── getIntersectionArea.js
│ ├── getIntersectionScore.js
│ ├── getOffsetDiff.js
│ ├── getPrefixedPropName.js
│ ├── getScrollableAncestors.js
│ ├── getStyle.js
│ ├── getStyleAsFloat.js
│ ├── getStyleName.js
│ ├── getTranslate.js
│ ├── getTranslateString.js
│ ├── getUnprefixedPropName.js
│ ├── hasPassiveEvents.js
│ ├── isFunction.js
│ ├── isNative.js
│ ├── isNodeList.js
│ ├── isOverlapping.js
│ ├── isPlainObject.js
│ ├── isScrollable.js
│ ├── isTransformed.js
│ ├── noop.js
│ ├── normalizeArrayIndex.js
│ ├── raf.js
│ ├── removeClass.js
│ ├── setStyles.js
│ ├── toArray.js
│ ├── transformProp.js
│ └── transformStyle.js
└── tests/
├── grid-constructor/
│ ├── container.js
│ └── instance.js
├── grid-events/
│ ├── add.js
│ ├── beforeReceive.js
│ ├── beforeSend.js
│ ├── destroy.js
│ ├── dragEnd.js
│ ├── dragInit.js
│ ├── dragMove.js
│ ├── dragReleaseEnd.js
│ ├── dragReleaseStart.js
│ ├── dragScroll.js
│ ├── dragStart.js
│ ├── draggerEvent.js
│ ├── filter.js
│ ├── hideEnd.js
│ ├── hideStart.js
│ ├── layoutAbort.js
│ ├── layoutEnd.js
│ ├── layoutStart.js
│ ├── move.js
│ ├── receive.js
│ ├── remove.js
│ ├── send.js
│ ├── showEnd.js
│ ├── showStart.js
│ ├── sort.js
│ └── synchronize.js
├── grid-methods/
│ ├── add.js
│ ├── destroy.js
│ ├── filter.js
│ ├── getElement.js
│ ├── getItems.js
│ ├── hide.js
│ ├── layout.js
│ ├── move.js
│ ├── off.js
│ ├── on.js
│ ├── refreshItems.js
│ ├── refreshSortData.js
│ ├── remove.js
│ ├── send.js
│ ├── show.js
│ ├── sort.js
│ └── synchronize.js
├── grid-options/
│ ├── containerClass.js
│ ├── dragAutoScroll.js
│ ├── dragAxis.js
│ ├── dragContainer.js
│ ├── dragEnabled.js
│ ├── dragPlaceholder.js
│ ├── dragSort.js
│ ├── dragSortPredicate.js
│ ├── dragStartPredicate.js
│ ├── hideDuration.js
│ ├── itemClass.js
│ ├── itemDraggingClass.js
│ ├── itemHiddenClass.js
│ ├── itemPositioningClass.js
│ ├── itemReleasingClass.js
│ ├── itemVisibleClass.js
│ ├── items.js
│ ├── layout.js
│ ├── showDuration.js
│ └── visibleStyles-hiddenStyles.js
├── index.js
├── item-methods/
│ ├── getElement.js
│ ├── getGrid.js
│ ├── getHeight.js
│ ├── getMargin.js
│ ├── getPosition.js
│ ├── getWidth.js
│ ├── isActive.js
│ ├── isDestroyed.js
│ ├── isDragging.js
│ ├── isHiding.js
│ ├── isPositioning.js
│ ├── isReleasing.js
│ ├── isShowing.js
│ └── isVisible.js
└── utils.js
SYMBOL INDEX (380 symbols across 64 files)
FILE: dist/muuri.js
function Emitter (line 71) | function Emitter() {
function EdgeHack (line 285) | function EdgeHack(dragger) {
function getPrefixedPropName (line 387) | function getPrefixedPropName(style, prop) {
function hasPassiveEvents (line 411) | function hasPassiveEvents() {
function Dragger (line 447) | function Dragger(element, cssProps) {
function Ticker (line 1005) | function Ticker(numLanes) {
function TickerLane (line 1062) | function TickerLane() {
function addLayoutTick (line 1108) | function addLayoutTick(itemId, read, write) {
function cancelLayoutTick (line 1113) | function cancelLayoutTick(itemId) {
function addVisibilityTick (line 1118) | function addVisibilityTick(itemId, read, write) {
function cancelVisibilityTick (line 1123) | function cancelVisibilityTick(itemId) {
function addDragStartTick (line 1128) | function addDragStartTick(itemId, read, write) {
function cancelDragStartTick (line 1133) | function cancelDragStartTick(itemId) {
function addDragMoveTick (line 1138) | function addDragMoveTick(itemId, read, write) {
function cancelDragMoveTick (line 1143) | function cancelDragMoveTick(itemId) {
function addDragScrollTick (line 1148) | function addDragScrollTick(itemId, read, write) {
function cancelDragScrollTick (line 1153) | function cancelDragScrollTick(itemId) {
function addDragSortTick (line 1158) | function addDragSortTick(itemId, read) {
function cancelDragSortTick (line 1162) | function cancelDragSortTick(itemId) {
function addPlaceholderLayoutTick (line 1166) | function addPlaceholderLayoutTick(itemId, read, write) {
function cancelPlaceholderLayoutTick (line 1171) | function cancelPlaceholderLayoutTick(itemId) {
function addPlaceholderResizeTick (line 1176) | function addPlaceholderResizeTick(itemId, write) {
function cancelPlaceholderResizeTick (line 1180) | function cancelPlaceholderResizeTick(itemId) {
function addAutoScrollTick (line 1184) | function addAutoScrollTick(read, write) {
function cancelAutoScrollTick (line 1189) | function cancelAutoScrollTick() {
function addDebounceTick (line 1194) | function addDebounceTick(debounceId, read) {
function cancelDebounceTick (line 1198) | function cancelDebounceTick(debounceId) {
function isFunction (line 1219) | function isFunction(val) {
function getStyle (line 1232) | function getStyle(element, style) {
function getStyleAsFloat (line 1251) | function getStyleAsFloat(el, style) {
function getScrollElement (line 1263) | function getScrollElement(element) {
function getScrollLeft (line 1275) | function getScrollLeft(element) {
function getScrollTop (line 1283) | function getScrollTop(element) {
function getScrollLeftMax (line 1291) | function getScrollLeftMax(element) {
function getScrollTopMax (line 1303) | function getScrollTopMax(element) {
function getContentRect (line 1319) | function getContentRect(element, result) {
function getItemAutoScrollSettings (line 1348) | function getItemAutoScrollSettings(item) {
function prepareItemScrollSync (line 1355) | function prepareItemScrollSync(item) {
function applyItemScrollSync (line 1363) | function applyItemScrollSync(item) {
function computeThreshold (line 1379) | function computeThreshold(threshold, safeZone, itemSize, targetSize) {
function ScrollRequest (line 1386) | function ScrollRequest() {
function ScrollAction (line 1484) | function ScrollAction() {
function Pool (line 1541) | function Pool(createItem, releaseItem) {
function isOverlapping (line 1568) | function isOverlapping(a, b) {
function getIntersectionArea (line 1584) | function getIntersectionArea(a, b) {
function getIntersectionScore (line 1599) | function getIntersectionScore(a, b) {
function AutoScroller (line 1624) | function AutoScroller() {
function elementMatches (line 2339) | function elementMatches(el, selector) {
function addClass (line 2349) | function addClass(element, className) {
function arrayInsert (line 2373) | function arrayInsert(array, items, index) {
function normalizeArrayIndex (line 2392) | function normalizeArrayIndex(array, index, sizeOffset) {
function arrayMove (line 2406) | function arrayMove(array, fromIndex, toIndex) {
function arraySwap (line 2429) | function arraySwap(array, index, withIndex) {
function getStyleName (line 2460) | function getStyleName(property) {
function isTransformed (line 2494) | function isTransformed(element) {
function getContainingBlock (line 2513) | function getContainingBlock(element) {
function getOffset (line 2541) | function getOffset(element, offsetData) {
function getOffsetDiff (line 2582) | function getOffsetDiff(elemA, elemB, compareContainingBlocks) {
function isScrollableOverflow (line 2613) | function isScrollableOverflow(value) {
function isScrollable (line 2623) | function isScrollable(element) {
function getScrollableAncestors (line 2640) | function getScrollableAncestors(element, result) {
function getTranslate (line 2681) | function getTranslate(element) {
function removeClass (line 2707) | function removeClass(element, className) {
function ItemDrag (line 2735) | function ItemDrag(item) {
function getTargetGrid (line 2902) | function getTargetGrid(item, rootGrid, threshold) {
function openAnchorHref (line 4156) | function openAnchorHref(element) {
function getCurrentStyles (line 4180) | function getCurrentStyles(element, styles) {
function getUnprefixedPropName (line 4207) | function getUnprefixedPropName(prop) {
function isNative (line 4231) | function isNative(feat) {
function setStyles (line 4247) | function setStyles(element, styles) {
function Animator (line 4262) | function Animator(element) {
function createFrame (line 4450) | function createFrame(props, prefix) {
function getTranslateString (line 4466) | function getTranslateString(x, y) {
function ItemDragPlaceholder (line 4476) | function ItemDragPlaceholder(item) {
function ItemDragRelease (line 4890) | function ItemDragRelease(item) {
function ItemLayout (line 5052) | function ItemLayout(item) {
function ItemMigrate (line 5347) | function ItemMigrate(item) {
function ItemVisibility (line 5616) | function ItemVisibility(item) {
function createUid (line 5924) | function createUid() {
function Item (line 5936) | function Item(grid, element, isActive) {
function createPackerProcessor (line 6323) | function createPackerProcessor(isWorker) {
function createWorkerProcessors (line 6865) | function createWorkerProcessors(amount, onmessage) {
function destroyWorkerProcessors (line 6888) | function destroyWorkerProcessors(workers) {
function isWorkerProcessorsSupported (line 6909) | function isWorkerProcessorsSupported() {
function Packer (line 6934) | function Packer(numWorkers, options) {
function debounce (line 7195) | function debounce(fn, durationMs) {
function isNodeList (line 7248) | function isNodeList(val) {
function isPlainObject (line 7263) | function isPlainObject(val) {
function noop (line 7267) | function noop() {}
function toArray (line 7275) | function toArray(val) {
function Grid (line 7353) | function Grid(element, options) {
function defaultComparer (line 8215) | function defaultComparer(a, b) {
function customComparer (line 8256) | function customComparer(a, b) {
function triggerVisibilityChange (line 8740) | function triggerVisibilityChange() {
function mergeSettings (line 8804) | function mergeSettings(defaultSettings, userSettings) {
function mergeObjects (line 8841) | function mergeObjects(target, source) {
function getInitialGridElements (line 8888) | function getInitialGridElements(gridElement, elements) {
function bindLayoutOnResize (line 8921) | function bindLayoutOnResize(grid, delay) {
function unbindLayoutOnResize (line 8940) | function unbindLayoutOnResize(grid) {
function normalizeStyles (line 8955) | function normalizeStyles(styles) {
function createIndexMap (line 8977) | function createIndexMap(items) {
function compareIndexMap (line 8993) | function compareIndexMap(indexMap, itemA, itemB) {
FILE: dist/muuri.module.js
function Emitter (line 65) | function Emitter() {
function EdgeHack (line 279) | function EdgeHack(dragger) {
function getPrefixedPropName (line 381) | function getPrefixedPropName(style, prop) {
function hasPassiveEvents (line 405) | function hasPassiveEvents() {
function Dragger (line 441) | function Dragger(element, cssProps) {
function Ticker (line 999) | function Ticker(numLanes) {
function TickerLane (line 1056) | function TickerLane() {
function addLayoutTick (line 1102) | function addLayoutTick(itemId, read, write) {
function cancelLayoutTick (line 1107) | function cancelLayoutTick(itemId) {
function addVisibilityTick (line 1112) | function addVisibilityTick(itemId, read, write) {
function cancelVisibilityTick (line 1117) | function cancelVisibilityTick(itemId) {
function addDragStartTick (line 1122) | function addDragStartTick(itemId, read, write) {
function cancelDragStartTick (line 1127) | function cancelDragStartTick(itemId) {
function addDragMoveTick (line 1132) | function addDragMoveTick(itemId, read, write) {
function cancelDragMoveTick (line 1137) | function cancelDragMoveTick(itemId) {
function addDragScrollTick (line 1142) | function addDragScrollTick(itemId, read, write) {
function cancelDragScrollTick (line 1147) | function cancelDragScrollTick(itemId) {
function addDragSortTick (line 1152) | function addDragSortTick(itemId, read) {
function cancelDragSortTick (line 1156) | function cancelDragSortTick(itemId) {
function addPlaceholderLayoutTick (line 1160) | function addPlaceholderLayoutTick(itemId, read, write) {
function cancelPlaceholderLayoutTick (line 1165) | function cancelPlaceholderLayoutTick(itemId) {
function addPlaceholderResizeTick (line 1170) | function addPlaceholderResizeTick(itemId, write) {
function cancelPlaceholderResizeTick (line 1174) | function cancelPlaceholderResizeTick(itemId) {
function addAutoScrollTick (line 1178) | function addAutoScrollTick(read, write) {
function cancelAutoScrollTick (line 1183) | function cancelAutoScrollTick() {
function addDebounceTick (line 1188) | function addDebounceTick(debounceId, read) {
function cancelDebounceTick (line 1192) | function cancelDebounceTick(debounceId) {
function isFunction (line 1213) | function isFunction(val) {
function getStyle (line 1226) | function getStyle(element, style) {
function getStyleAsFloat (line 1245) | function getStyleAsFloat(el, style) {
function getScrollElement (line 1257) | function getScrollElement(element) {
function getScrollLeft (line 1269) | function getScrollLeft(element) {
function getScrollTop (line 1277) | function getScrollTop(element) {
function getScrollLeftMax (line 1285) | function getScrollLeftMax(element) {
function getScrollTopMax (line 1297) | function getScrollTopMax(element) {
function getContentRect (line 1313) | function getContentRect(element, result) {
function getItemAutoScrollSettings (line 1342) | function getItemAutoScrollSettings(item) {
function prepareItemScrollSync (line 1349) | function prepareItemScrollSync(item) {
function applyItemScrollSync (line 1357) | function applyItemScrollSync(item) {
function computeThreshold (line 1373) | function computeThreshold(threshold, safeZone, itemSize, targetSize) {
function ScrollRequest (line 1380) | function ScrollRequest() {
function ScrollAction (line 1478) | function ScrollAction() {
function Pool (line 1535) | function Pool(createItem, releaseItem) {
function isOverlapping (line 1562) | function isOverlapping(a, b) {
function getIntersectionArea (line 1578) | function getIntersectionArea(a, b) {
function getIntersectionScore (line 1593) | function getIntersectionScore(a, b) {
function AutoScroller (line 1618) | function AutoScroller() {
function elementMatches (line 2333) | function elementMatches(el, selector) {
function addClass (line 2343) | function addClass(element, className) {
function arrayInsert (line 2367) | function arrayInsert(array, items, index) {
function normalizeArrayIndex (line 2386) | function normalizeArrayIndex(array, index, sizeOffset) {
function arrayMove (line 2400) | function arrayMove(array, fromIndex, toIndex) {
function arraySwap (line 2423) | function arraySwap(array, index, withIndex) {
function getStyleName (line 2454) | function getStyleName(property) {
function isTransformed (line 2488) | function isTransformed(element) {
function getContainingBlock (line 2507) | function getContainingBlock(element) {
function getOffset (line 2535) | function getOffset(element, offsetData) {
function getOffsetDiff (line 2576) | function getOffsetDiff(elemA, elemB, compareContainingBlocks) {
function isScrollableOverflow (line 2607) | function isScrollableOverflow(value) {
function isScrollable (line 2617) | function isScrollable(element) {
function getScrollableAncestors (line 2634) | function getScrollableAncestors(element, result) {
function getTranslate (line 2675) | function getTranslate(element) {
function removeClass (line 2701) | function removeClass(element, className) {
function ItemDrag (line 2729) | function ItemDrag(item) {
function getTargetGrid (line 2896) | function getTargetGrid(item, rootGrid, threshold) {
function openAnchorHref (line 4150) | function openAnchorHref(element) {
function getCurrentStyles (line 4174) | function getCurrentStyles(element, styles) {
function getUnprefixedPropName (line 4201) | function getUnprefixedPropName(prop) {
function isNative (line 4225) | function isNative(feat) {
function setStyles (line 4241) | function setStyles(element, styles) {
function Animator (line 4256) | function Animator(element) {
function createFrame (line 4444) | function createFrame(props, prefix) {
function getTranslateString (line 4460) | function getTranslateString(x, y) {
function ItemDragPlaceholder (line 4470) | function ItemDragPlaceholder(item) {
function ItemDragRelease (line 4884) | function ItemDragRelease(item) {
function ItemLayout (line 5046) | function ItemLayout(item) {
function ItemMigrate (line 5341) | function ItemMigrate(item) {
function ItemVisibility (line 5610) | function ItemVisibility(item) {
function createUid (line 5918) | function createUid() {
function Item (line 5930) | function Item(grid, element, isActive) {
function createPackerProcessor (line 6317) | function createPackerProcessor(isWorker) {
function createWorkerProcessors (line 6859) | function createWorkerProcessors(amount, onmessage) {
function destroyWorkerProcessors (line 6882) | function destroyWorkerProcessors(workers) {
function isWorkerProcessorsSupported (line 6903) | function isWorkerProcessorsSupported() {
function Packer (line 6928) | function Packer(numWorkers, options) {
function debounce (line 7189) | function debounce(fn, durationMs) {
function isNodeList (line 7242) | function isNodeList(val) {
function isPlainObject (line 7257) | function isPlainObject(val) {
function noop (line 7261) | function noop() {}
function toArray (line 7269) | function toArray(val) {
function Grid (line 7347) | function Grid(element, options) {
function defaultComparer (line 8209) | function defaultComparer(a, b) {
function customComparer (line 8250) | function customComparer(a, b) {
function triggerVisibilityChange (line 8734) | function triggerVisibilityChange() {
function mergeSettings (line 8798) | function mergeSettings(defaultSettings, userSettings) {
function mergeObjects (line 8835) | function mergeObjects(target, source) {
function getInitialGridElements (line 8882) | function getInitialGridElements(gridElement, elements) {
function bindLayoutOnResize (line 8915) | function bindLayoutOnResize(grid, delay) {
function unbindLayoutOnResize (line 8934) | function unbindLayoutOnResize(grid) {
function normalizeStyles (line 8949) | function normalizeStyles(styles) {
function createIndexMap (line 8971) | function createIndexMap(items) {
function compareIndexMap (line 8987) | function compareIndexMap(indexMap, itemA, itemB) {
FILE: docs/.vitepress/config.js
function nav (line 43) | function nav() {
function sidebarGuide (line 61) | function sidebarGuide() {
FILE: rollup.config.js
method transform (line 5) | transform(code) {
FILE: src/Animator/Animator.js
function Animator (line 22) | function Animator(element) {
function createFrame (line 210) | function createFrame(props, prefix) {
FILE: src/AutoScroller/AutoScroller.js
function AutoScroller (line 46) | function AutoScroller() {
FILE: src/AutoScroller/Pool.js
function Pool (line 8) | function Pool(createItem, releaseItem) {
FILE: src/AutoScroller/ScrollAction.js
function ScrollAction (line 11) | function ScrollAction() {
FILE: src/AutoScroller/ScrollRequest.js
function ScrollRequest (line 12) | function ScrollRequest() {
FILE: src/AutoScroller/utils.js
function getScrollElement (line 18) | function getScrollElement(element) {
function getScrollLeft (line 30) | function getScrollLeft(element) {
function getScrollTop (line 38) | function getScrollTop(element) {
function getScrollLeftMax (line 46) | function getScrollLeftMax(element) {
function getScrollTopMax (line 58) | function getScrollTopMax(element) {
function getContentRect (line 74) | function getContentRect(element, result) {
function getItemAutoScrollSettings (line 103) | function getItemAutoScrollSettings(item) {
function prepareItemScrollSync (line 110) | function prepareItemScrollSync(item) {
function applyItemScrollSync (line 118) | function applyItemScrollSync(item) {
function computeThreshold (line 134) | function computeThreshold(threshold, safeZone, itemSize, targetSize) {
FILE: src/Dragger/Dragger.js
function Dragger (line 36) | function Dragger(element, cssProps) {
FILE: src/Dragger/EdgeHack.js
function EdgeHack (line 29) | function EdgeHack(dragger) {
FILE: src/Emitter/Emitter.js
function Emitter (line 13) | function Emitter() {
FILE: src/Grid/Grid.js
function Grid (line 134) | function Grid(element, options) {
function defaultComparer (line 996) | function defaultComparer(a, b) {
function customComparer (line 1037) | function customComparer(a, b) {
function triggerVisibilityChange (line 1521) | function triggerVisibilityChange() {
function mergeSettings (line 1585) | function mergeSettings(defaultSettings, userSettings) {
function mergeObjects (line 1622) | function mergeObjects(target, source) {
function getInitialGridElements (line 1669) | function getInitialGridElements(gridElement, elements) {
function bindLayoutOnResize (line 1702) | function bindLayoutOnResize(grid, delay) {
function unbindLayoutOnResize (line 1721) | function unbindLayoutOnResize(grid) {
function normalizeStyles (line 1736) | function normalizeStyles(styles) {
function createIndexMap (line 1758) | function createIndexMap(items) {
function compareIndexMap (line 1774) | function compareIndexMap(indexMap, itemA, itemB) {
FILE: src/Item/Item.js
function Item (line 33) | function Item(grid, element, isActive) {
FILE: src/Item/ItemDrag.js
function ItemDrag (line 66) | function ItemDrag(item) {
function getTargetGrid (line 233) | function getTargetGrid(item, rootGrid, threshold) {
function openAnchorHref (line 1487) | function openAnchorHref(element) {
FILE: src/Item/ItemDragPlaceholder.js
function ItemDragPlaceholder (line 37) | function ItemDragPlaceholder(item) {
FILE: src/Item/ItemDragRelease.js
function ItemDragRelease (line 22) | function ItemDragRelease(item) {
FILE: src/Item/ItemLayout.js
function ItemLayout (line 26) | function ItemLayout(item) {
FILE: src/Item/ItemMigrate.js
function ItemMigrate (line 24) | function ItemMigrate(item) {
FILE: src/Item/ItemVisibility.js
function ItemVisibility (line 23) | function ItemVisibility(item) {
FILE: src/Packer/Packer.js
function Packer (line 35) | function Packer(numWorkers, options) {
FILE: src/Packer/PackerProcessor.js
function createPackerProcessor (line 8) | function createPackerProcessor(isWorker) {
function createWorkerProcessors (line 551) | function createWorkerProcessors(amount, onmessage) {
function destroyWorkerProcessors (line 574) | function destroyWorkerProcessors(workers) {
function isWorkerProcessorsSupported (line 595) | function isWorkerProcessorsSupported() {
FILE: src/Ticker/Ticker.js
function Ticker (line 15) | function Ticker(numLanes) {
function TickerLane (line 72) | function TickerLane() {
FILE: src/index.d.ts
type StyleDeclaration (line 1) | interface StyleDeclaration {
type EventListener (line 5) | type EventListener = (...args: any[]) => any;
type DraggerCssProps (line 7) | interface DraggerCssProps {
type DraggerEvent (line 16) | interface DraggerEvent {
type DraggerStartEvent (line 36) | interface DraggerStartEvent extends DraggerEvent {
type DraggerMoveEvent (line 46) | interface DraggerMoveEvent extends DraggerEvent {
type DraggerEndEvent (line 52) | interface DraggerEndEvent extends DraggerEvent {
type DraggerCancelEvent (line 58) | interface DraggerCancelEvent extends DraggerEvent {
type DraggerEvents (line 64) | interface DraggerEvents {
type ScrollEvent (line 71) | interface ScrollEvent extends Event {
type GridEvents (line 75) | interface GridEvents {
type LayoutData (line 121) | interface LayoutData {
type LayoutOptions (line 129) | interface LayoutOptions {
type LayoutOnFinish (line 137) | type LayoutOnFinish = (items: Item[], isAborted: boolean) => any;
type LayoutFunctionCallback (line 139) | type LayoutFunctionCallback = (layout: LayoutData) => any;
type LayoutFunctionCancel (line 141) | type LayoutFunctionCancel = (...args: any[]) => any;
type LayoutFunction (line 143) | type LayoutFunction = (
type SortDataGetter (line 152) | type SortDataGetter = (item: Item, element: HTMLElement) => any;
type DragStartPredicate (line 154) | type DragStartPredicate = (
type DragStartPredicateOptions (line 159) | interface DragStartPredicateOptions {
type DragSortGetter (line 164) | type DragSortGetter = (this: Grid, item: Item) => Grid[] | null | void |...
type DragSortHeuristics (line 166) | interface DragSortHeuristics {
type DragSortPredicateResult (line 172) | type DragSortPredicateResult = {
type DragSortPredicate (line 178) | type DragSortPredicate = (item: Item, event: DraggerMoveEvent) => DragSo...
type DragSortPredicateOptions (line 180) | interface DragSortPredicateOptions {
type DragReleaseOptions (line 186) | interface DragReleaseOptions {
type DragPlaceholderCreateElement (line 192) | type DragPlaceholderCreateElement = (item: Item) => HTMLElement;
type DragPlaceholderOnCreate (line 194) | type DragPlaceholderOnCreate = (item: Item, placeholderElement: HTMLElem...
type DragPlaceholderOnRemove (line 196) | type DragPlaceholderOnRemove = (item: Item, placeholderElement: HTMLElem...
type DragPlaceholderOptions (line 198) | interface DragPlaceholderOptions {
type DragAutoScrollTarget (line 205) | interface DragAutoScrollTarget {
type DragAutoScrollTargets (line 212) | type DragAutoScrollTargets = Array<Window | HTMLElement | DragAutoScroll...
type DragAutoScrollTargetsGetter (line 214) | type DragAutoScrollTargetsGetter = (item: Item) => DragAutoScrollTargets;
type DragAutoScrollOnStart (line 216) | type DragAutoScrollOnStart = (
type DragAutoScrollOnStop (line 222) | type DragAutoScrollOnStop = (
type DragAutoScrollHandle (line 228) | type DragAutoScrollHandle = (
type DragAutoScrollSpeed (line 243) | type DragAutoScrollSpeed = (
type DragAutoScrollOptions (line 259) | interface DragAutoScrollOptions {
type GridOptions (line 271) | interface GridOptions {
class Item (line 311) | class Item {
class ItemLayout (line 329) | class ItemLayout {
class ItemVisibility (line 336) | class ItemVisibility {
class ItemMigrate (line 345) | class ItemMigrate {
class ItemDrag (line 352) | class ItemDrag {
class ItemDragRelease (line 369) | class ItemDragRelease {
class ItemDragPlaceholder (line 377) | class ItemDragPlaceholder {
class Emitter (line 387) | class Emitter {
class Animator (line 398) | class Animator {
class Dragger (line 414) | class Dragger {
class AutoScroller (line 428) | class AutoScroller {
class Packer (line 453) | class Packer {
class Grid (line 468) | class Grid {
FILE: src/ticker.js
function addLayoutTick (line 34) | function addLayoutTick(itemId, read, write) {
function cancelLayoutTick (line 39) | function cancelLayoutTick(itemId) {
function addVisibilityTick (line 44) | function addVisibilityTick(itemId, read, write) {
function cancelVisibilityTick (line 49) | function cancelVisibilityTick(itemId) {
function addDragStartTick (line 54) | function addDragStartTick(itemId, read, write) {
function cancelDragStartTick (line 59) | function cancelDragStartTick(itemId) {
function addDragMoveTick (line 64) | function addDragMoveTick(itemId, read, write) {
function cancelDragMoveTick (line 69) | function cancelDragMoveTick(itemId) {
function addDragScrollTick (line 74) | function addDragScrollTick(itemId, read, write) {
function cancelDragScrollTick (line 79) | function cancelDragScrollTick(itemId) {
function addDragSortTick (line 84) | function addDragSortTick(itemId, read) {
function cancelDragSortTick (line 88) | function cancelDragSortTick(itemId) {
function addPlaceholderLayoutTick (line 92) | function addPlaceholderLayoutTick(itemId, read, write) {
function cancelPlaceholderLayoutTick (line 97) | function cancelPlaceholderLayoutTick(itemId) {
function addPlaceholderResizeTick (line 102) | function addPlaceholderResizeTick(itemId, write) {
function cancelPlaceholderResizeTick (line 106) | function cancelPlaceholderResizeTick(itemId) {
function addAutoScrollTick (line 110) | function addAutoScrollTick(read, write) {
function cancelAutoScrollTick (line 115) | function cancelAutoScrollTick() {
function addDebounceTick (line 120) | function addDebounceTick(debounceId, read) {
function cancelDebounceTick (line 124) | function cancelDebounceTick(debounceId) {
FILE: src/utils/addClass.js
function addClass (line 15) | function addClass(element, className) {
FILE: src/utils/arrayInsert.js
function arrayInsert (line 19) | function arrayInsert(array, items, index) {
FILE: src/utils/arrayMove.js
function arrayMove (line 18) | function arrayMove(array, fromIndex, toIndex) {
FILE: src/utils/arraySwap.js
function arraySwap (line 18) | function arraySwap(array, index, withIndex) {
FILE: src/utils/createUid.js
function createUid (line 13) | function createUid() {
FILE: src/utils/debounce.js
function debounce (line 22) | function debounce(fn, durationMs) {
FILE: src/utils/elementMatches.js
function elementMatches (line 26) | function elementMatches(el, selector) {
FILE: src/utils/getContainingBlock.js
function getContainingBlock (line 19) | function getContainingBlock(element) {
FILE: src/utils/getCurrentStyles.js
function getCurrentStyles (line 17) | function getCurrentStyles(element, styles) {
FILE: src/utils/getIntersectionArea.js
function getIntersectionArea (line 16) | function getIntersectionArea(a, b) {
FILE: src/utils/getIntersectionScore.js
function getIntersectionScore (line 17) | function getIntersectionScore(a, b) {
FILE: src/utils/getOffsetDiff.js
function getOffset (line 26) | function getOffset(element, offsetData) {
function getOffsetDiff (line 67) | function getOffsetDiff(elemA, elemB, compareContainingBlocks) {
FILE: src/utils/getPrefixedPropName.js
function getPrefixedPropName (line 18) | function getPrefixedPropName(style, prop) {
FILE: src/utils/getScrollableAncestors.js
function getScrollableAncestors (line 18) | function getScrollableAncestors(element, result) {
FILE: src/utils/getStyle.js
function getStyle (line 16) | function getStyle(element, style) {
FILE: src/utils/getStyleAsFloat.js
function getStyleAsFloat (line 17) | function getStyleAsFloat(el, style) {
FILE: src/utils/getStyleName.js
function getStyleName (line 19) | function getStyleName(property) {
FILE: src/utils/getTranslate.js
function getTranslate (line 25) | function getTranslate(element) {
FILE: src/utils/getTranslateString.js
function getTranslateString (line 15) | function getTranslateString(x, y) {
FILE: src/utils/getUnprefixedPropName.js
function getUnprefixedPropName (line 16) | function getUnprefixedPropName(prop) {
FILE: src/utils/hasPassiveEvents.js
function hasPassiveEvents (line 13) | function hasPassiveEvents() {
FILE: src/utils/isFunction.js
function isFunction (line 15) | function isFunction(val) {
FILE: src/utils/isNative.js
function isNative (line 18) | function isNative(feat) {
FILE: src/utils/isNodeList.js
function isNodeList (line 16) | function isNodeList(val) {
FILE: src/utils/isOverlapping.js
function isOverlapping (line 14) | function isOverlapping(a, b) {
FILE: src/utils/isPlainObject.js
function isPlainObject (line 17) | function isPlainObject(val) {
FILE: src/utils/isScrollable.js
function isScrollableOverflow (line 15) | function isScrollableOverflow(value) {
function isScrollable (line 25) | function isScrollable(element) {
FILE: src/utils/isTransformed.js
function isTransformed (line 27) | function isTransformed(element) {
FILE: src/utils/noop.js
function noop (line 7) | function noop() {}
FILE: src/utils/normalizeArrayIndex.js
function normalizeArrayIndex (line 18) | function normalizeArrayIndex(array, index, sizeOffset) {
FILE: src/utils/removeClass.js
function removeClass (line 15) | function removeClass(element, className) {
FILE: src/utils/setStyles.js
function setStyles (line 13) | function setStyles(element, styles) {
FILE: src/utils/toArray.js
function toArray (line 15) | function toArray(val) {
FILE: tests/grid-events/draggerEvent.js
function assertDraggerEvent (line 21) | function assertDraggerEvent(draggerEvent) {
FILE: tests/grid-options/dragSortPredicate.js
function onMove (line 26) | function onMove(data) {
function onMove (line 64) | function onMove(data) {
function onMove (line 152) | function onMove() {
function onMove (line 196) | function onMove(data) {
function onMove (line 242) | function onMove(data) {
function onMove (line 286) | function onMove(data) {
FILE: tests/grid-options/dragStartPredicate.js
function onDragStart (line 70) | function onDragStart() {
function onDragMove (line 79) | function onDragMove() {
function onDragEnd (line 88) | function onDragEnd() {
function onDragReleaseStart (line 97) | function onDragReleaseStart() {
FILE: tests/item-methods/isDragging.js
function onDragStart (line 19) | function onDragStart() {
function onDragMove (line 28) | function onDragMove() {
function onDragEnd (line 37) | function onDragEnd() {
FILE: tests/item-methods/isReleasing.js
function onDragStart (line 19) | function onDragStart() {
function onDragMove (line 28) | function onDragMove() {
function onDragEnd (line 37) | function onDragEnd() {
function onDragReleaseStart (line 46) | function onDragReleaseStart() {
function onDragReleaseEnd (line 55) | function onDragReleaseEnd() {
Condensed preview — 175 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,283K chars).
[
{
"path": ".eslintrc.js",
"chars": 2002,
"preview": "module.exports = {\n parserOptions: {\n ecmaVersion: 7,\n sourceType: 'module'\n },\n rules: {\n // Possible error"
},
{
"path": ".github/FUNDING.yml",
"chars": 21,
"preview": "github: [niklasramo]\n"
},
{
"path": ".gitignore",
"chars": 90,
"preview": "node_modules\r\nwebsite\r\n/coverage\r\n/website\r\n*.log\r\n*.env\r\n*.DS_Store\ndocs/.vitepress/dist\n"
},
{
"path": ".prettierrc.json",
"chars": 47,
"preview": "{\n \"printWidth\": 100,\n \"singleQuote\": true\n}\n"
},
{
"path": ".travis.yml",
"chars": 1466,
"preview": "language: node_js\r\nnode_js:\r\n- '12'\r\nenv:\r\n email: false\r\n global:\r\n - secure: UcHda6DVxvFcQPscXNG/6jd0pwVJoik0wx7Ald"
},
{
"path": "AUTHORS.txt",
"chars": 300,
"preview": "Authors ordered by first contribution.\n\nNiklas Rämö <inramo@gmail.com>\nIndigane <https://github.com/indigane>\nJason Holl"
},
{
"path": "CONTRIBUTING.md",
"chars": 3776,
"preview": "# Contributing to Muuri\n\nThanks for the interest in contributing to Muuri! Here you will find some instructions on how t"
},
{
"path": "LICENSE.md",
"chars": 1076,
"preview": "\r\nCopyright (c) 2015, Haltu Oy\r\n\r\nPermission is hereby granted, free of charge, to any person obtaining a copy of\r\nthis "
},
{
"path": "README.md",
"chars": 109471,
"preview": "<h1 align=\"center\">\r\n <a id=\"muuri\" href=\"#muuri\" aria-hidden=\"true\"><img src=\"https://muuri.dev/muuri-logo.svg\" alt=\"M"
},
{
"path": "dist/muuri.js",
"chars": 264371,
"preview": "/**\n* Muuri v0.9.5\n* https://muuri.dev/\n* Copyright (c) 2015-present, Haltu Oy\n* Released under the MIT license\n* https:"
},
{
"path": "dist/muuri.module.js",
"chars": 248497,
"preview": "/**\n* Muuri v0.9.5\n* https://muuri.dev/\n* Copyright (c) 2015-present, Haltu Oy\n* Released under the MIT license\n* https:"
},
{
"path": "docs/.vitepress/config.js",
"chars": 1865,
"preview": "import { version } from '../../package.json';\n\nexport default {\n title: 'Muuri Docs',\n description: 'Documentation for"
},
{
"path": "docs/.vitepress/theme/custom.css",
"chars": 171,
"preview": ":root {\n --vp-c-brand: #ED6EE3;\n --vp-c-brand-light: #ED6EE3;\n --vp-c-brand-lighter:#FA73EF;\n --vp-c-green-d"
},
{
"path": "docs/.vitepress/theme/index.js",
"chars": 97,
"preview": "import DefaultTheme from 'vitepress/theme';\nimport './custom.css';\n\nexport default DefaultTheme;\n"
},
{
"path": "docs/examples.md",
"chars": 7432,
"preview": "# Examples\n\n## Barebones Grid\n\n<iframe height=\"400\" style=\"width: 100%;\" scrolling=\"no\" title=\"Muuri: barebones grid\" sr"
},
{
"path": "docs/getting-started.md",
"chars": 4004,
"preview": "# Getting Started\n\n## 1. Get Muuri\n\nInstall via [npm](https://www.npmjs.com/package/muuri):\n\n```bash\nnpm install muuri\n`"
},
{
"path": "docs/grid-constructor.md",
"chars": 3154,
"preview": "# Grid Constructor\n\n`Muuri` is a constructor function and should be always instantiated with the `new` keyword. For the "
},
{
"path": "docs/grid-events.md",
"chars": 12961,
"preview": "# Grid Events\n\n## synchronize\n\nTriggered after item elements are synchronized via `grid.synchronize()`.\n\n**Examples**\n\n`"
},
{
"path": "docs/grid-methods.md",
"chars": 23349,
"preview": "# Grid Methods\n\n## getElement\n\n`grid.getElement()`\n\nGet the grid element.\n\n**Returns** — _element_\n\n**"
},
{
"path": "docs/grid-options.md",
"chars": 43434,
"preview": "# Grid Options\n\n## items\n\nThe initial item elements, which should be children of the grid element. All elements that are"
},
{
"path": "docs/index.md",
"chars": 422,
"preview": "# Muuri Docs\n\nYou've stumbled on the official documentation site of [Muuri](https://muuri.dev), a JavaScript layout engi"
},
{
"path": "docs/item-methods.md",
"chars": 3170,
"preview": "# Item Methods\n\n## getGrid\n\n`item.getGrid()`\n\nGet the grid instance the item belongs to.\n\n**Returns** — "
},
{
"path": "gulpfile.js",
"chars": 3743,
"preview": "const fs = require('fs');\r\nconst gulp = require('gulp');\r\nconst eslint = require('gulp-eslint');\r\nconst karma = require("
},
{
"path": "karma.conf.js",
"chars": 127,
"preview": "const defaultSettings = require('./karma.defaults.js');\n\nmodule.exports = function (config) {\n config.set(defaultSettin"
},
{
"path": "karma.defaults.js",
"chars": 1412,
"preview": "const pkg = require('./package.json');\n\nmodule.exports = {\n basePath: '',\n frameworks: ['qunit'],\n plugins: ['karma-q"
},
{
"path": "package.json",
"chars": 2381,
"preview": "{\n \"name\": \"muuri\",\n \"version\": \"0.9.5\",\n \"description\": \"Responsive, sortable, filterable and draggable layouts\",\n "
},
{
"path": "rollup.banner.js",
"chars": 570,
"preview": "const pkg = require('./package.json');\n\nmodule.exports = `/**\n* Muuri v${pkg.version}\n* ${pkg.homepage}\n* Copyright (c) "
},
{
"path": "rollup.config.js",
"chars": 523,
"preview": "const pkg = require('./package.json');\nconst banner = require('./rollup.banner.js');\n\nconst stripBanner = {\n transform("
},
{
"path": "src/Animator/Animator.js",
"chars": 5792,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/AutoScroller/AutoScroller.js",
"chars": 20800,
"preview": "/**\n * Muuri AutoScroller\n * Copyright (c) 2019-present, Niklas Rämö <inramo@gmail.com>\n * Released under the MIT licens"
},
{
"path": "src/AutoScroller/LICENSE.md",
"chars": 1056,
"preview": "Copyright (c) 2019, Niklas Rämö\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis so"
},
{
"path": "src/AutoScroller/Pool.js",
"chars": 638,
"preview": "/**\n * Muuri AutoScroller\n * Copyright (c) 2019-present, Niklas Rämö <inramo@gmail.com>\n * Released under the MIT licens"
},
{
"path": "src/AutoScroller/ScrollAction.js",
"chars": 1785,
"preview": "/**\n * Muuri AutoScroller\n * Copyright (c) 2019-present, Niklas Rämö <inramo@gmail.com>\n * Released under the MIT licens"
},
{
"path": "src/AutoScroller/ScrollRequest.js",
"chars": 3223,
"preview": "/**\n * Muuri AutoScroller\n * Copyright (c) 2019-present, Niklas Rämö <inramo@gmail.com>\n * Released under the MIT licens"
},
{
"path": "src/AutoScroller/constants.js",
"chars": 441,
"preview": "/**\n * Muuri AutoScroller\n * Copyright (c) 2019-present, Niklas Rämö <inramo@gmail.com>\n * Released under the MIT licens"
},
{
"path": "src/AutoScroller/utils.js",
"chars": 3609,
"preview": "/**\n * Muuri AutoScroller\n * Copyright (c) 2019-present, Niklas Rämö <inramo@gmail.com>\n * Released under the MIT licens"
},
{
"path": "src/Dragger/Dragger.js",
"chars": 15468,
"preview": "/**\n * Muuri Dragger\n * Copyright (c) 2018-present, Niklas Rämö <inramo@gmail.com>\n * Released under the MIT license\n * "
},
{
"path": "src/Dragger/EdgeHack.js",
"chars": 3449,
"preview": "/**\n * Muuri Dragger\n * Copyright (c) 2018-present, Niklas Rämö <inramo@gmail.com>\n * Released under the MIT license\n * "
},
{
"path": "src/Dragger/LICENSE.md",
"chars": 1056,
"preview": "Copyright (c) 2018, Niklas Rämö\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis so"
},
{
"path": "src/Emitter/Emitter.js",
"chars": 5247,
"preview": "/**\n * Muuri Emitter\n * Copyright (c) 2018-present, Niklas Rämö <inramo@gmail.com>\n * Released under the MIT license\n * "
},
{
"path": "src/Emitter/LICENSE.md",
"chars": 1056,
"preview": "Copyright (c) 2018, Niklas Rämö\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis so"
},
{
"path": "src/Grid/Grid.js",
"chars": 50991,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/Item/Item.js",
"chars": 10149,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/Item/ItemDrag.js",
"chars": 43690,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/Item/ItemDragPlaceholder.js",
"chars": 11778,
"preview": "/**\n * Copyright (c) 2018-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/Item/ItemDragRelease.js",
"chars": 4695,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/Item/ItemLayout.js",
"chars": 8580,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/Item/ItemMigrate.js",
"chars": 7841,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/Item/ItemVisibility.js",
"chars": 8977,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/Packer/LICENSE.md",
"chars": 1056,
"preview": "Copyright (c) 2016, Niklas Rämö\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis so"
},
{
"path": "src/Packer/Packer.js",
"chars": 7807,
"preview": "/**\n * Muuri Packer\n * Copyright (c) 2016-present, Niklas Rämö <inramo@gmail.com>\n * Released under the MIT license\n * h"
},
{
"path": "src/Packer/PackerProcessor.js",
"chars": 18171,
"preview": "/**\n * Muuri Packer\n * Copyright (c) 2016-present, Niklas Rämö <inramo@gmail.com>\n * Released under the MIT license\n * h"
},
{
"path": "src/Ticker/LICENSE.md",
"chars": 1056,
"preview": "Copyright (c) 2018, Niklas Rämö\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis so"
},
{
"path": "src/Ticker/Ticker.js",
"chars": 2257,
"preview": "/**\n * Muuri Ticker\n * Copyright (c) 2018-present, Niklas Rämö <inramo@gmail.com>\n * Released under the MIT license\n * h"
},
{
"path": "src/constants.js",
"chars": 1587,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/index.d.ts",
"chars": 15252,
"preview": "export interface StyleDeclaration {\n [styleProperty: string]: string;\n}\n\nexport type EventListener = (...args: any[]) ="
},
{
"path": "src/index.js",
"chars": 179,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/ticker.js",
"chars": 4028,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/addClass.js",
"chars": 555,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/arrayInsert.js",
"chars": 739,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/arrayMove.js",
"chars": 858,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/arraySwap.js",
"chars": 882,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/createUid.js",
"chars": 309,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/debounce.js",
"chars": 1466,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/elementMatches.js",
"chars": 638,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/getContainingBlock.js",
"chars": 1022,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/getCurrentStyles.js",
"chars": 774,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/getIntersectionArea.js",
"chars": 604,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/getIntersectionScore.js",
"chars": 643,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/getOffsetDiff.js",
"chars": 2797,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/getPrefixedPropName.js",
"chars": 1009,
"preview": "/**\n * Forked from hammer.js:\n * https://github.com/hammerjs/hammer.js/blob/563b5b1e4bfbb5796798dd286cd57b7c56f1eb9e/src"
},
{
"path": "src/utils/getScrollableAncestors.js",
"chars": 1143,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/getStyle.js",
"chars": 627,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/getStyleAsFloat.js",
"chars": 458,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/getStyleName.js",
"chars": 1083,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/getTranslate.js",
"chars": 1195,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/getTranslateString.js",
"chars": 418,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/getUnprefixedPropName.js",
"chars": 629,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/hasPassiveEvents.js",
"chars": 730,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/isFunction.js",
"chars": 340,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/isNative.js",
"chars": 591,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/isNodeList.js",
"chars": 498,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/isOverlapping.js",
"chars": 454,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/isPlainObject.js",
"chars": 472,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/isScrollable.js",
"chars": 743,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/isTransformed.js",
"chars": 1056,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/noop.js",
"chars": 174,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/normalizeArrayIndex.js",
"chars": 834,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/raf.js",
"chars": 525,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/removeClass.js",
"chars": 641,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/setStyles.js",
"chars": 373,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/toArray.js",
"chars": 412,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/transformProp.js",
"chars": 330,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "src/utils/transformStyle.js",
"chars": 311,
"preview": "/**\n * Copyright (c) 2015-present, Haltu Oy\n * Released under the MIT license\n * https://github.com/haltu/muuri/blob/mas"
},
{
"path": "tests/grid-constructor/container.js",
"chars": 2620,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid instance');\n\n QUnit.test('Muuri constructor shou"
},
{
"path": "tests/grid-constructor/instance.js",
"chars": 242,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid instance');\n\n QUnit.test('Muuri should be a glob"
},
{
"path": "tests/grid-events/add.js",
"chars": 973,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid events');\n\n QUnit.test('add: should be triggered"
},
{
"path": "tests/grid-events/beforeReceive.js",
"chars": 6559,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid events');\n\n QUnit.test(\n 'beforeReceive: shou"
},
{
"path": "tests/grid-events/beforeSend.js",
"chars": 6545,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid events');\n\n QUnit.test(\n 'beforeSend: should "
},
{
"path": "tests/grid-events/destroy.js",
"chars": 764,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid events');\n\n QUnit.test('destroy: should be trigg"
},
{
"path": "tests/grid-events/dragEnd.js",
"chars": 1819,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid events');\n\n QUnit.test('dragEnd: should be trigg"
},
{
"path": "tests/grid-events/dragInit.js",
"chars": 2032,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid events');\n\n QUnit.test(\n 'dragInit: should be"
},
{
"path": "tests/grid-events/dragMove.js",
"chars": 1724,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid events');\n\n QUnit.test('dragMove: should be trig"
},
{
"path": "tests/grid-events/dragReleaseEnd.js",
"chars": 909,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid events');\n\n QUnit.test('dragReleaseEnd: should b"
},
{
"path": "tests/grid-events/dragReleaseStart.js",
"chars": 963,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid events');\n\n QUnit.test('dragReleaseStart: should"
},
{
"path": "tests/grid-events/dragScroll.js",
"chars": 1631,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid events');\n\n QUnit.test('dragScroll: should be tr"
},
{
"path": "tests/grid-events/dragStart.js",
"chars": 2029,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid events');\n\n QUnit.test(\n 'dragStart: should b"
},
{
"path": "tests/grid-events/draggerEvent.js",
"chars": 3662,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid events');\n\n QUnit.test('draggerEvent interface',"
},
{
"path": "tests/grid-events/filter.js",
"chars": 1139,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid events');\n\n QUnit.test('filter: should be trigge"
},
{
"path": "tests/grid-events/hideEnd.js",
"chars": 1073,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid events');\n\n QUnit.test(\n 'hideEnd: should be "
},
{
"path": "tests/grid-events/hideStart.js",
"chars": 1030,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid events');\n\n QUnit.test(\n 'hideStart: should b"
},
{
"path": "tests/grid-events/layoutAbort.js",
"chars": 1395,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid events');\n\n QUnit.test(\n 'layoutAbort: should"
},
{
"path": "tests/grid-events/layoutEnd.js",
"chars": 1477,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid events');\n\n QUnit.test(\n 'layoutEnd: should b"
},
{
"path": "tests/grid-events/layoutStart.js",
"chars": 1985,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid events');\n\n QUnit.test(\n 'layoutStart: should"
},
{
"path": "tests/grid-events/move.js",
"chars": 3398,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid events');\n\n QUnit.test('move: should be triggere"
},
{
"path": "tests/grid-events/receive.js",
"chars": 6561,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid events');\n\n QUnit.test(\n 'receive: should be "
},
{
"path": "tests/grid-events/remove.js",
"chars": 1024,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid events');\n\n QUnit.test('remove: should be trigge"
},
{
"path": "tests/grid-events/send.js",
"chars": 6544,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid events');\n\n QUnit.test(\n 'send: should be tri"
},
{
"path": "tests/grid-events/showEnd.js",
"chars": 1078,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid events');\n\n QUnit.test(\n 'showEnd: should be "
},
{
"path": "tests/grid-events/showStart.js",
"chars": 1039,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid events');\n\n QUnit.test(\n 'showStart: should b"
},
{
"path": "tests/grid-events/sort.js",
"chars": 1089,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid events');\n\n QUnit.test('sort: should be triggere"
},
{
"path": "tests/grid-events/synchronize.js",
"chars": 588,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid events');\n\n QUnit.test('synchronize: should be t"
},
{
"path": "tests/grid-methods/add.js",
"chars": 5560,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n var idList = utils.idList;\n\n QUnit.module('Grid methods');\n\n QUnit."
},
{
"path": "tests/grid-methods/destroy.js",
"chars": 455,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid methods');\n\n QUnit.test('destroy: should return "
},
{
"path": "tests/grid-methods/filter.js",
"chars": 5064,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n var idList = utils.idList;\n\n QUnit.module('Grid methods');\n\n QUnit."
},
{
"path": "tests/grid-methods/getElement.js",
"chars": 475,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid methods');\n\n QUnit.test('getElement: should retu"
},
{
"path": "tests/grid-methods/getItems.js",
"chars": 1569,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n var idList = utils.idList;\n\n QUnit.module('Grid methods');\n\n QUnit."
},
{
"path": "tests/grid-methods/hide.js",
"chars": 3721,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n var idList = utils.idList;\n\n QUnit.module('Grid methods');\n\n QUnit."
},
{
"path": "tests/grid-methods/layout.js",
"chars": 2655,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n var idList = utils.idList;\n\n QUnit.module('Grid methods');\n\n QUnit."
},
{
"path": "tests/grid-methods/move.js",
"chars": 3643,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n var idList = utils.idList;\n\n QUnit.module('Grid methods');\n\n QUnit."
},
{
"path": "tests/grid-methods/off.js",
"chars": 1257,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid methods');\n\n QUnit.test('off: should return the "
},
{
"path": "tests/grid-methods/on.js",
"chars": 1051,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid methods');\n\n QUnit.test('on: should return the i"
},
{
"path": "tests/grid-methods/refreshItems.js",
"chars": 2213,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid methods');\n\n QUnit.test('refreshItems: should re"
},
{
"path": "tests/grid-methods/refreshSortData.js",
"chars": 471,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid methods');\n\n QUnit.test('refreshSortData: should"
},
{
"path": "tests/grid-methods/remove.js",
"chars": 4617,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n var idList = utils.idList;\n\n QUnit.module('Grid methods');\n\n QUnit."
},
{
"path": "tests/grid-methods/send.js",
"chars": 4857,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid methods');\n\n QUnit.test('send: should return the"
},
{
"path": "tests/grid-methods/show.js",
"chars": 3858,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n var idList = utils.idList;\n\n QUnit.module('Grid methods');\n\n QUnit."
},
{
"path": "tests/grid-methods/sort.js",
"chars": 6380,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n var idList = utils.idList;\n\n QUnit.module('Grid methods');\n\n QUnit."
},
{
"path": "tests/grid-methods/synchronize.js",
"chars": 1311,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid methods');\n\n QUnit.test('synchronize: should ret"
},
{
"path": "tests/grid-options/containerClass.js",
"chars": 553,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid options');\n\n QUnit.test('containerClass: should "
},
{
"path": "tests/grid-options/dragAutoScroll.js",
"chars": 4633,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid options');\n\n QUnit.test('dragAutoScroll: should "
},
{
"path": "tests/grid-options/dragAxis.js",
"chars": 4653,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid options');\n\n QUnit.test('dragAxis: should allow "
},
{
"path": "tests/grid-options/dragContainer.js",
"chars": 1562,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid options');\n\n QUnit.test('dragContainer: should b"
},
{
"path": "tests/grid-options/dragEnabled.js",
"chars": 1397,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid options');\n\n QUnit.test('dragEnabled: drag shoul"
},
{
"path": "tests/grid-options/dragPlaceholder.js",
"chars": 5527,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid options');\n\n QUnit.test('dragPlaceholder: should"
},
{
"path": "tests/grid-options/dragSort.js",
"chars": 5252,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid options');\n\n QUnit.test('dragSort: should be ena"
},
{
"path": "tests/grid-options/dragSortPredicate.js",
"chars": 7520,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid options');\n\n QUnit.test('dragSortPredicate: the "
},
{
"path": "tests/grid-options/dragStartPredicate.js",
"chars": 7856,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid options');\n\n QUnit.test(\n 'dragStartPredicate"
},
{
"path": "tests/grid-options/hideDuration.js",
"chars": 672,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid options');\n\n QUnit.test('hideDuration: should di"
},
{
"path": "tests/grid-options/itemClass.js",
"chars": 553,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid options');\n\n QUnit.test('itemClass: should defin"
},
{
"path": "tests/grid-options/itemDraggingClass.js",
"chars": 1299,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid options');\n\n QUnit.test('itemDraggingClass: shou"
},
{
"path": "tests/grid-options/itemHiddenClass.js",
"chars": 833,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid options');\n\n QUnit.test('itemHiddenClass: should"
},
{
"path": "tests/grid-options/itemPositioningClass.js",
"chars": 1185,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid options');\n\n QUnit.test(\n 'itemPositioningCla"
},
{
"path": "tests/grid-options/itemReleasingClass.js",
"chars": 1385,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid options');\n\n QUnit.test(\n 'itemReleasingClass"
},
{
"path": "tests/grid-options/itemVisibleClass.js",
"chars": 836,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid options');\n\n QUnit.test('itemVisibleClass: shoul"
},
{
"path": "tests/grid-options/items.js",
"chars": 2684,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid options');\n\n QUnit.test('items: should fetch all"
},
{
"path": "tests/grid-options/layout.js",
"chars": 16825,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid options');\n\n QUnit.test('layout: vertical - left"
},
{
"path": "tests/grid-options/showDuration.js",
"chars": 706,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid options');\n\n QUnit.test('showDuration: should di"
},
{
"path": "tests/grid-options/visibleStyles-hiddenStyles.js",
"chars": 1138,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Grid options');\n\n QUnit.test(\n 'visibleStyles/hidd"
},
{
"path": "tests/index.js",
"chars": 151,
"preview": "QUnit.config.reorder = false;\n\nMuuri.syncPacker = new Muuri.Packer();\nMuuri.asyncPacker = Muuri.defaultPacker;\nMuuri.def"
},
{
"path": "tests/item-methods/getElement.js",
"chars": 584,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Item methods');\n\n QUnit.test('getElement: should retu"
},
{
"path": "tests/item-methods/getGrid.js",
"chars": 517,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Item methods');\n\n QUnit.test('getGrid: should return "
},
{
"path": "tests/item-methods/getHeight.js",
"chars": 944,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Item methods');\n\n QUnit.test(\n 'getHeight: should "
},
{
"path": "tests/item-methods/getMargin.js",
"chars": 884,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Item methods');\n\n QUnit.test('getMargin: should retur"
},
{
"path": "tests/item-methods/getPosition.js",
"chars": 1001,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Item methods');\n\n QUnit.test(\n 'getPosition: shoul"
},
{
"path": "tests/item-methods/getWidth.js",
"chars": 935,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Item methods');\n\n QUnit.test(\n 'getWidth: should r"
},
{
"path": "tests/item-methods/isActive.js",
"chars": 773,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Item methods');\n\n QUnit.test('isActive: should return"
},
{
"path": "tests/item-methods/isDestroyed.js",
"chars": 848,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Item methods');\n\n QUnit.test(\n 'isDestroyed: shoul"
},
{
"path": "tests/item-methods/isDragging.js",
"chars": 1536,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Item methods');\n\n QUnit.test('isDragging: should retu"
},
{
"path": "tests/item-methods/isHiding.js",
"chars": 1350,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Item methods');\n\n QUnit.test(\n 'isHiding: should r"
},
{
"path": "tests/item-methods/isPositioning.js",
"chars": 1185,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Item methods');\n\n QUnit.test(\n 'isPositioning: sho"
},
{
"path": "tests/item-methods/isReleasing.js",
"chars": 2164,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Item methods');\n\n QUnit.test('isReleasing: should ret"
},
{
"path": "tests/item-methods/isShowing.js",
"chars": 1616,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Item methods');\n\n QUnit.test(\n 'isShowing: should "
},
{
"path": "tests/item-methods/isVisible.js",
"chars": 803,
"preview": "(function (window) {\n var Muuri = window.Muuri;\n\n QUnit.module('Item methods');\n\n QUnit.test('isVisible: should retur"
},
{
"path": "tests/utils.js",
"chars": 5661,
"preview": "(function(window) {\n var utils = (window.utils = {});\n var supportsTouch = !!('TouchEvent' in window);\n var supportsP"
}
]
About this extraction
This page contains the full source code of the haltu/muuri GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 175 files (1.2 MB), approximately 309.3k tokens, and a symbol index with 380 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.