Full Code of axyz/perfect-layout for AI

master 398db7e01e71 cached
12 files
24.2 KB
6.5k tokens
28 symbols
1 requests
Download .txt
Repository: axyz/perfect-layout
Branch: master
Commit: 398db7e01e71
Files: 12
Total size: 24.2 KB

Directory structure:
gitextract_q68n26jo/

├── .gitignore
├── LICENSE
├── README.md
├── bower.json
├── dist/
│   ├── jquery.perfectLayout.js
│   └── perfectLayout.js
├── index.js
├── jqueryPlugin.js
├── lib/
│   ├── .tern-port
│   ├── BSTLinearPartition.js
│   └── BreakpointPartition.js
└── package.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# Logs
logs
*.log

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2015 Andrea Moretti

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
================================================
# Perfect Layout

[Medium Article](https://medium.com/@axyz/in-search-of-the-perfect-image-gallery-34f46f7615a1)

[DEMO](http://codepen.io/axyz/full/VLJrKr/)

given an array of images in the form

```
{
  data: WATHEVER_YOU_WANT,
  src: "path/to/image.jpg",
  ratio: 1.5
}
```

returns an array of images in the form
```
{
  data: WATHEVER_YOU_WANT,
  src: "path/to/image.jpg",
  width: WIDTH,
  height: HEIGHT
}
```

where WIDTH and HEIGHT are the dimensions that image must have to fit the layout.

## Usage

on node
```
$ npm install --save perfect-layout
```
and
```
var perfectLayout = require('perfect-layout')
```
while on the browser you can just
```
<script src="perfectLayout.min.js"></script>
```
then
```
var perfectRows = perfectLayout(photos, width, height, {
  // default options
  margin: 0
});
```

### Options

- margin: [number]
If you are going to use a css margin for your images you need to specify it here
as well, so that the layout will adapt to scale down the images accordingly.

## Motivations

This was inspired by [chromatic.io](http://www.chromatic.io/FQrLQsb) galleries
and I want to credit the [@crispymtn](https://github.com/crispymtn) team for the
original implementation.

This version aim to be more lightweight using a greedy algorithm instead of the
optimal one and also leave to the user the responsability to choose how to
manipulate the DOM accordingly to the returned array.

## Example jQuery plugin

for convenience a jquery plugin is included for a basic usage.

assuming that a global `window.photos` array exists as specified above

```
<div id="gallery"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.js"></script>
<script src="jquery.perfectLayout.min.js"></script>

<script>
$(document).ready(function() {
  $('#gallery').perfectGallery(photos);

  $(window).resize(function() {
    $('#gallery').perfectGallery(photos);
  });
});

</script>
```

*N.B.* Please note that this is only an example on how to use the `perfectLayout` function.
The jQuery plugin is not to be used in production as it do not provide any
crossbrowser optimization, at the time of writing it should however correctly
work on the latest chrome and firefox browsers on linux.

For custom behaviour give a look at the [jqueryPlugin.js](https://github.com/axyz/perfect-layout/blob/master/jqueryPlugin.js) 
and use it as a starting point to generate the desired DOM nodes.

the data field can be used to populate the images with any needed metadata
you may need and is probably a good idea to provide it from your backend.

## Changelog

## [v1.2.0]
### Changed
- using breakPointPartition thanks to @GreenGremlin

## [v1.1.1]
### Changed
- using BST based linear partitioning instead of greedy one
- huge speed improvement
- the resulting set is now optimal
### Fixed
- the partition will now keep the same order as the input array
- the layout should now be equal to the chromatic.io one in all the cases

## [v1.1.0]
### Added
- margin option

## [v1.0.0]
### Initial Release
<!--stackedit_data:
eyJoaXN0b3J5IjpbLTE0OTUwNzc5OTksLTExNjE2ODIwOTFdfQ
==
-->

================================================
FILE: bower.json
================================================
{
  "name": "perfect-layout",
  "description": "Image layout generator based on linear partitioning",
  "main": "dist/perfectLayout.min.js",
  "version": "1.0.0",
  "authors": [
    "Andrea Moretti <axyzxp@gmail.com>"
  ],
  "moduleType": [
    "es6",
    "globals",
    "node"
  ],
  "keywords": [
    "gallery",
    "images",
    "layout",
    "linear",
    "partition"
  ],
  "license": "MIT",
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "test",
    "tests"
  ]
}


================================================
FILE: dist/jquery.perfectLayout.js
================================================
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});
exports['default'] = perfectLayout;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _libBreakpointPartitionJs = require('./lib/BreakpointPartition.js');

var _libBreakpointPartitionJs2 = _interopRequireDefault(_libBreakpointPartitionJs);

function perfectLayout(photos, screenWidth, screenHeight, opts) {
  opts = opts || {};
  opts.margin = opts.margin || 0;

  var rows = _perfectRowsNumber(photos, screenWidth, screenHeight);
  var idealHeight = parseInt(screenHeight / 2, 10);

  if (rows < 1) {
    return photos.map(function (img) {
      return {
        data: img.data,
        src: img.src,
        width: parseInt(idealHeight * img.ratio) - opts.margin * 2,
        height: idealHeight
      };
    });
  } else {
    var _ret = (function () {
      var weights = photos.map(function (img) {
        return parseInt(img.ratio * 100, 10);
      });
      var partitions = (0, _libBreakpointPartitionJs2['default'])(weights, rows);

      var current = 0;

      return {
        v: partitions.map(function (row) {
          var summedRatios = row.reduce(function (sum, el, i) {
            return sum + photos[current + i].ratio;
          }, 0);

          return row.map(function () {
            var img = photos[current++];

            return {
              data: img.data,
              src: img.src,
              width: parseInt(screenWidth / summedRatios * img.ratio, 10) - opts.margin * 2,
              height: parseInt(screenWidth / summedRatios, 10)
            };
          });
        })
      };
    })();

    if (typeof _ret === 'object') return _ret.v;
  }
}

function _perfectRowsNumber(photos, screenWidth, screenHeight) {
  var idealHeight = parseInt(screenHeight / 2);
  var totalWidth = photos.reduce(function (sum, img) {
    return sum + img.ratio * idealHeight;
  }, 0);
  return Math.round(totalWidth / screenWidth);
}
module.exports = exports['default'];

},{"./lib/BreakpointPartition.js":3}],2:[function(require,module,exports){
'use strict';

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _ = require('.');

var _2 = _interopRequireDefault(_);

$.fn.perfectLayout = function (photos) {
  var node = this;
  var scrollBarSize = $('html').hasClass('touch') ? 0 : 15;
  var perfectRows = (0, _2['default'])(photos, window.innerWidth - scrollBarSize, $(window).height());
  node.empty();

  perfectRows.forEach(function (row) {
    row.forEach(function (img) {
      var imgNode = $('<div style="float: left;" class="image"></div>');
      imgNode.css({
        'width': img.width + 'px',
        'height': img.height + 'px',
        'background': 'url(' + img.src + ')',
        'background-size': 'cover'
      });
      node.append(imgNode);
    });
  });
};

},{".":1}],3:[function(require,module,exports){
// Rather than blindly perform a binary search from the maximum width. It starts
// from the ideal width (The ideal width being the width if the images fit
// perfectly in the given container.) and expands to the next width that will
// allow an item to move up a row. This algorithm will find the exact width that
// produces the "ideal" layout and should generally find it in two or three
// passes.
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});
exports['default'] = BreakpointPartition;

function BreakpointPartition(imageRatioSequence, expectedRowCount) {
  if (imageRatioSequence.length <= 1) return [imageRatioSequence];
  if (expectedRowCount >= imageRatioSequence.length) return imageRatioSequence.map(function (item) {
    return [item];
  });

  var layoutWidth = findLayoutWidth(imageRatioSequence, expectedRowCount);
  var currentRow = 0;

  return imageRatioSequence.reduce(function (rows, imageRatio) {
    if (sum(rows[currentRow]) + imageRatio > layoutWidth) currentRow++;
    rows[currentRow].push(imageRatio);
    return rows;
    // waiting for more elegant solutions (Array.fill) to work correctly
  }, new Array(expectedRowCount).join().split(',').map(function () {
    return [];
  }));
}

// starting at the ideal width, expand to the next breakpoint until we find
// a width that produces the expected number of rows
function findLayoutWidth(imageRatioSequence, expectedRowCount) {
  var idealWidth = sum(imageRatioSequence) / expectedRowCount;
  var widestItem = Math.max.apply(null, imageRatioSequence);
  var galleryWidth = Math.max(idealWidth, widestItem);
  var layout = getLayoutDetails(imageRatioSequence, galleryWidth);

  while (layout.rowCount > expectedRowCount) {
    galleryWidth += layout.nextBreakpoint;

    layout = getLayoutDetails(imageRatioSequence, galleryWidth);
  }
  return galleryWidth;
}

// find the
function getLayoutDetails(imageRatioSequence, expectedWidth) {
  var startingLayout = {
    currentRowWidth: 0,
    rowCount: 1,
    // the largest possible step to the next breakpoint is the smallest image ratio
    nextBreakpoint: Math.min.apply(null, imageRatioSequence)
  };

  var finalLayout = imageRatioSequence.reduce(function (layout, itemWidth) {
    var rowWidth = layout.currentRowWidth + itemWidth;
    var currentRowsNextBreakpoint = undefined;

    if (rowWidth > expectedWidth) {
      currentRowsNextBreakpoint = rowWidth - expectedWidth;
      if (currentRowsNextBreakpoint < layout.nextBreakpoint) {
        layout.nextBreakpoint = currentRowsNextBreakpoint;
      }
      layout.rowCount += 1;
      layout.currentRowWidth = itemWidth;
    } else {
      layout.currentRowWidth = rowWidth;
    }

    return layout;
  }, startingLayout);
  return {
    rowCount: finalLayout.rowCount,
    nextBreakpoint: finalLayout.nextBreakpoint
  };
}

function sum(arr) {
  return arr.reduce(function (sum, el) {
    return sum + el;
  }, 0);
}
module.exports = exports['default'];

},{}]},{},[2]);


================================================
FILE: dist/perfectLayout.js
================================================
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.perfectLayout = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});
exports['default'] = perfectLayout;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _libBreakpointPartitionJs = require('./lib/BreakpointPartition.js');

var _libBreakpointPartitionJs2 = _interopRequireDefault(_libBreakpointPartitionJs);

function perfectLayout(photos, screenWidth, screenHeight, opts) {
  opts = opts || {};
  opts.margin = opts.margin || 0;

  var rows = _perfectRowsNumber(photos, screenWidth, screenHeight);
  var idealHeight = parseInt(screenHeight / 2, 10);

  if (rows < 1) {
    return photos.map(function (img) {
      return {
        data: img.data,
        src: img.src,
        width: parseInt(idealHeight * img.ratio) - opts.margin * 2,
        height: idealHeight
      };
    });
  } else {
    var _ret = (function () {
      var weights = photos.map(function (img) {
        return parseInt(img.ratio * 100, 10);
      });
      var partitions = (0, _libBreakpointPartitionJs2['default'])(weights, rows);

      var current = 0;

      return {
        v: partitions.map(function (row) {
          var summedRatios = row.reduce(function (sum, el, i) {
            return sum + photos[current + i].ratio;
          }, 0);

          return row.map(function () {
            var img = photos[current++];

            return {
              data: img.data,
              src: img.src,
              width: parseInt(screenWidth / summedRatios * img.ratio, 10) - opts.margin * 2,
              height: parseInt(screenWidth / summedRatios, 10)
            };
          });
        })
      };
    })();

    if (typeof _ret === 'object') return _ret.v;
  }
}

function _perfectRowsNumber(photos, screenWidth, screenHeight) {
  var idealHeight = parseInt(screenHeight / 2);
  var totalWidth = photos.reduce(function (sum, img) {
    return sum + img.ratio * idealHeight;
  }, 0);
  return Math.round(totalWidth / screenWidth);
}
module.exports = exports['default'];

},{"./lib/BreakpointPartition.js":2}],2:[function(require,module,exports){
// Rather than blindly perform a binary search from the maximum width. It starts
// from the ideal width (The ideal width being the width if the images fit
// perfectly in the given container.) and expands to the next width that will
// allow an item to move up a row. This algorithm will find the exact width that
// produces the "ideal" layout and should generally find it in two or three
// passes.
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});
exports['default'] = BreakpointPartition;

function BreakpointPartition(imageRatioSequence, expectedRowCount) {
  if (imageRatioSequence.length <= 1) return [imageRatioSequence];
  if (expectedRowCount >= imageRatioSequence.length) return imageRatioSequence.map(function (item) {
    return [item];
  });

  var layoutWidth = findLayoutWidth(imageRatioSequence, expectedRowCount);
  var currentRow = 0;

  return imageRatioSequence.reduce(function (rows, imageRatio) {
    if (sum(rows[currentRow]) + imageRatio > layoutWidth) currentRow++;
    rows[currentRow].push(imageRatio);
    return rows;
    // waiting for more elegant solutions (Array.fill) to work correctly
  }, new Array(expectedRowCount).join().split(',').map(function () {
    return [];
  }));
}

// starting at the ideal width, expand to the next breakpoint until we find
// a width that produces the expected number of rows
function findLayoutWidth(imageRatioSequence, expectedRowCount) {
  var idealWidth = sum(imageRatioSequence) / expectedRowCount;
  var widestItem = Math.max.apply(null, imageRatioSequence);
  var galleryWidth = Math.max(idealWidth, widestItem);
  var layout = getLayoutDetails(imageRatioSequence, galleryWidth);

  while (layout.rowCount > expectedRowCount) {
    galleryWidth += layout.nextBreakpoint;

    layout = getLayoutDetails(imageRatioSequence, galleryWidth);
  }
  return galleryWidth;
}

// find the
function getLayoutDetails(imageRatioSequence, expectedWidth) {
  var startingLayout = {
    currentRowWidth: 0,
    rowCount: 1,
    // the largest possible step to the next breakpoint is the smallest image ratio
    nextBreakpoint: Math.min.apply(null, imageRatioSequence)
  };

  var finalLayout = imageRatioSequence.reduce(function (layout, itemWidth) {
    var rowWidth = layout.currentRowWidth + itemWidth;
    var currentRowsNextBreakpoint = undefined;

    if (rowWidth > expectedWidth) {
      currentRowsNextBreakpoint = rowWidth - expectedWidth;
      if (currentRowsNextBreakpoint < layout.nextBreakpoint) {
        layout.nextBreakpoint = currentRowsNextBreakpoint;
      }
      layout.rowCount += 1;
      layout.currentRowWidth = itemWidth;
    } else {
      layout.currentRowWidth = rowWidth;
    }

    return layout;
  }, startingLayout);
  return {
    rowCount: finalLayout.rowCount,
    nextBreakpoint: finalLayout.nextBreakpoint
  };
}

function sum(arr) {
  return arr.reduce(function (sum, el) {
    return sum + el;
  }, 0);
}
module.exports = exports['default'];

},{}]},{},[1])(1)
});

================================================
FILE: index.js
================================================
import BreakpointPartition from './lib/BreakpointPartition.js';

export default function perfectLayout(photos, screenWidth, screenHeight, opts) {
  opts = opts || {};
  opts.margin = opts.margin || 0;

  const rows = _perfectRowsNumber(photos, screenWidth, screenHeight);
  const idealHeight = parseInt(screenHeight / 2, 10);

  if (rows < 1) {
    return photos.map(img => {
      return {
        data: img.data,
        src: img.src,
        width: parseInt(idealHeight * img.ratio) - (opts.margin * 2),
        height: idealHeight
      };
    });
  } else {
    const weights = photos.map(img => parseInt(img.ratio * 100, 10));
    const partitions = BreakpointPartition(weights, rows);

    let current = 0;

    return partitions.map(row => {
      const summedRatios = row.reduce((sum, el, i) => sum + photos[current + i].ratio, 0);

      return row.map(() => {
        const img = photos[current++];

        return {
          data: img.data,
          src: img.src,
          width: parseInt((screenWidth / summedRatios) * img.ratio, 10) - (opts.margin * 2),
          height: parseInt(screenWidth / summedRatios, 10)
        };
      });
    });
  }
}

function _perfectRowsNumber(photos, screenWidth, screenHeight) {
  const idealHeight = parseInt(screenHeight / 2);
  const totalWidth = photos.reduce((sum, img) => sum + img.ratio * idealHeight, 0);
  return Math.round(totalWidth / screenWidth);
}


================================================
FILE: jqueryPlugin.js
================================================
import perfectLayout from '.';

$.fn.perfectLayout = function(photos) {
  const node = this;
  const scrollBarSize = $('html').hasClass('touch') ? 0 : 15;
  const perfectRows = perfectLayout(photos, window.innerWidth - scrollBarSize, $(window).height());
  node.empty();

  perfectRows.forEach(function (row) {
    row.forEach(function (img) {
      var imgNode = $('<div style="float: left;" class="image"></div>');
      imgNode.css({
        'width': img.width + 'px',
        'height': img.height + 'px',
        'background': 'url(' + img.src + ')',
        'background-size': 'cover'
      });
      node.append(imgNode);
    });
  });
};


================================================
FILE: lib/.tern-port
================================================
34063

================================================
FILE: lib/BSTLinearPartition.js
================================================
export default function BSTLinearPartition(seq, k) {
  if (seq.length <= 1) return [seq];
  if (k >= seq.length) return seq.map(el => [el]);

  const limit = threshold(seq, k);
  let current = 0;

  return seq.reduce((res, el) => {
    if (sum(res[current]) + el > limit) current++;
    res[current].push(el);
    return res;
    // waiting for more elegant solutions (Array.fill) to work correctly
  }, new Array(k).join().split(',').map(() => []));
}

// find the perfect limit that we should not pass when adding elements
// to a single partition.
function threshold(seq, k) {
  let bottom = max(seq);
  let top = sum(seq);

  while (bottom < top) {
    const mid = bottom + ( top - bottom) / 2;

    if (requiredElements(seq, mid) <= k) {
      top = mid;
    } else {
      bottom = mid + 1;
    }
  }
  return bottom;
}

// find how many elements from [seq] we cann group together stating below
// [limit] by adding their weights
function requiredElements(seq, limit) {
  return seq.reduce((res, el) => {
    res.tot += el;
    if (res.tot > limit) {
      res.tot = el;
      res.n++;
    }
    return res;
  }, {tot: 0, n: 1}).n;
}

function sum(arr) {
  return arr.reduce((sum, el) => sum + el, 0);
}

function max(arr) {
  return arr.reduce((max, el) => el > max ? el : max, 0);
}


================================================
FILE: lib/BreakpointPartition.js
================================================
// Rather than blindly perform a binary search from the maximum width. It starts
// from the ideal width (The ideal width being the width if the images fit
// perfectly in the given container.) and expands to the next width that will
// allow an item to move up a row. This algorithm will find the exact width that
// produces the "ideal" layout and should generally find it in two or three
// passes.
export default function BreakpointPartition(imageRatioSequence, expectedRowCount) {
  if (imageRatioSequence.length <= 1) return [imageRatioSequence];
  if (expectedRowCount >= imageRatioSequence.length)
    return imageRatioSequence.map(item => [item]);

  const layoutWidth = findLayoutWidth(imageRatioSequence, expectedRowCount);
  let currentRow = 0;

  return imageRatioSequence.reduce((rows, imageRatio) => {
    if (sum(rows[currentRow]) + imageRatio > layoutWidth) currentRow++;
    rows[currentRow].push(imageRatio);
    return rows;
    // waiting for more elegant solutions (Array.fill) to work correctly
  }, new Array(expectedRowCount).join().split(',').map(() => []));
}

// starting at the ideal width, expand to the next breakpoint until we find
// a width that produces the expected number of rows
function findLayoutWidth(imageRatioSequence, expectedRowCount) {
  let idealWidth = sum(imageRatioSequence) / expectedRowCount;
  let widestItem = Math.max.apply(null, imageRatioSequence);
  let galleryWidth = Math.max(idealWidth, widestItem);
  let layout = getLayoutDetails(imageRatioSequence, galleryWidth);

  while (layout.rowCount > expectedRowCount) {
    galleryWidth += layout.nextBreakpoint;

    layout = getLayoutDetails(imageRatioSequence, galleryWidth);
  }
  return galleryWidth;
}

// find the
function getLayoutDetails(imageRatioSequence, expectedWidth) {
  const startingLayout = {
    currentRowWidth: 0,
    rowCount: 1,
    // the largest possible step to the next breakpoint is the smallest image ratio
    nextBreakpoint: Math.min.apply(null, imageRatioSequence)
  };

  const finalLayout = imageRatioSequence.reduce((layout, itemWidth) => {
    const rowWidth = layout.currentRowWidth + itemWidth;
    let currentRowsNextBreakpoint;

    if (rowWidth > expectedWidth) {
      currentRowsNextBreakpoint = rowWidth - expectedWidth;
      if (currentRowsNextBreakpoint < layout.nextBreakpoint) {
        layout.nextBreakpoint = currentRowsNextBreakpoint;
      }
      layout.rowCount += 1;
      layout.currentRowWidth = itemWidth;
    } else {
      layout.currentRowWidth = rowWidth;
    }

    return layout;
  }, startingLayout);
  return {
    rowCount: finalLayout.rowCount,
    nextBreakpoint: finalLayout.nextBreakpoint
  };
}

function sum(arr) {
  return arr.reduce((sum, el) => sum + el, 0);
}


================================================
FILE: package.json
================================================
{
  "name": "perfect-layout",
  "version": "1.2.1",
  "description": "Image layout generator based on linear partitioning",
  "main": "index.js",
  "scripts": {
    "build": "./node_modules/.bin/browserify index.js -t babelify -t uglifyify --standalone perfectLayout -o dist/perfectLayout.min.js",
    "build-dev": "./node_modules/.bin/browserify index.js -t babelify --standalone perfectLayout -o dist/perfectLayout.js",
    "build-jquery": "npm run build && ./node_modules/.bin/browserify jqueryPlugin.js -t babelify -t uglifyify -o dist/jquery.perfectLayout.min.js",
    "build-jquery-dev": "npm run build-dev && ./node_modules/.bin/browserify jqueryPlugin.js -t babelify -o dist/jquery.perfectLayout.js",
    "test": "echo \"Error: no test specified\" && exit 1",
    "dist": "npm run build && npm run build-jquery && npm run build-dev && npm run build-jquery-dev"
  },
  "author": "Andrea Moretti (@axyz) <axyzxp@gmail.com>",
  "license": "MIT",
  "devDependencies": {
    "babelify": "^6.2.0",
    "browserify": "^11.0.1",
    "uglifyify": "^3.0.1"
  }
}
Download .txt
gitextract_q68n26jo/

├── .gitignore
├── LICENSE
├── README.md
├── bower.json
├── dist/
│   ├── jquery.perfectLayout.js
│   └── perfectLayout.js
├── index.js
├── jqueryPlugin.js
├── lib/
│   ├── .tern-port
│   ├── BSTLinearPartition.js
│   └── BreakpointPartition.js
└── package.json
Download .txt
SYMBOL INDEX (28 symbols across 5 files)

FILE: dist/jquery.perfectLayout.js
  function s (line 1) | function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&re...
  function _interopRequireDefault (line 9) | function _interopRequireDefault(obj) { return obj && obj.__esModule ? ob...
  function perfectLayout (line 15) | function perfectLayout(photos, screenWidth, screenHeight, opts) {
  function _perfectRowsNumber (line 64) | function _perfectRowsNumber(photos, screenWidth, screenHeight) {
  function _interopRequireDefault (line 76) | function _interopRequireDefault(obj) { return obj && obj.__esModule ? ob...
  function BreakpointPartition (line 116) | function BreakpointPartition(imageRatioSequence, expectedRowCount) {
  function findLayoutWidth (line 137) | function findLayoutWidth(imageRatioSequence, expectedRowCount) {
  function getLayoutDetails (line 152) | function getLayoutDetails(imageRatioSequence, expectedWidth) {
  function sum (line 183) | function sum(arr) {

FILE: dist/perfectLayout.js
  function s (line 1) | function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&re...
  function _interopRequireDefault (line 9) | function _interopRequireDefault(obj) { return obj && obj.__esModule ? ob...
  function perfectLayout (line 15) | function perfectLayout(photos, screenWidth, screenHeight, opts) {
  function _perfectRowsNumber (line 64) | function _perfectRowsNumber(photos, screenWidth, screenHeight) {
  function BreakpointPartition (line 87) | function BreakpointPartition(imageRatioSequence, expectedRowCount) {
  function findLayoutWidth (line 108) | function findLayoutWidth(imageRatioSequence, expectedRowCount) {
  function getLayoutDetails (line 123) | function getLayoutDetails(imageRatioSequence, expectedWidth) {
  function sum (line 154) | function sum(arr) {

FILE: index.js
  function perfectLayout (line 3) | function perfectLayout(photos, screenWidth, screenHeight, opts) {
  function _perfectRowsNumber (line 42) | function _perfectRowsNumber(photos, screenWidth, screenHeight) {

FILE: lib/BSTLinearPartition.js
  function BSTLinearPartition (line 1) | function BSTLinearPartition(seq, k) {
  function threshold (line 18) | function threshold(seq, k) {
  function requiredElements (line 36) | function requiredElements(seq, limit) {
  function sum (line 47) | function sum(arr) {
  function max (line 51) | function max(arr) {

FILE: lib/BreakpointPartition.js
  function BreakpointPartition (line 7) | function BreakpointPartition(imageRatioSequence, expectedRowCount) {
  function findLayoutWidth (line 25) | function findLayoutWidth(imageRatioSequence, expectedRowCount) {
  function getLayoutDetails (line 40) | function getLayoutDetails(imageRatioSequence, expectedWidth) {
  function sum (line 71) | function sum(arr) {
Condensed preview — 12 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (26K chars).
[
  {
    "path": ".gitignore",
    "chars": 526,
    "preview": "# Logs\nlogs\n*.log\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nl"
  },
  {
    "path": "LICENSE",
    "chars": 1082,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2015 Andrea Moretti\n\nPermission is hereby granted, free of charge, to any person ob"
  },
  {
    "path": "README.md",
    "chars": 3110,
    "preview": "# Perfect Layout\n\n[Medium Article](https://medium.com/@axyz/in-search-of-the-perfect-image-gallery-34f46f7615a1)\n\n[DEMO]"
  },
  {
    "path": "bower.json",
    "chars": 498,
    "preview": "{\n  \"name\": \"perfect-layout\",\n  \"description\": \"Image layout generator based on linear partitioning\",\n  \"main\": \"dist/pe"
  },
  {
    "path": "dist/jquery.perfectLayout.js",
    "chars": 6415,
    "preview": "(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require==\"function\"&&require;if(!u&&a)return a(o,!0)"
  },
  {
    "path": "dist/perfectLayout.js",
    "chars": 5957,
    "preview": "(function(f){if(typeof exports===\"object\"&&typeof module!==\"undefined\"){module.exports=f()}else if(typeof define===\"func"
  },
  {
    "path": "index.js",
    "chars": 1414,
    "preview": "import BreakpointPartition from './lib/BreakpointPartition.js';\n\nexport default function perfectLayout(photos, screenWid"
  },
  {
    "path": "jqueryPlugin.js",
    "chars": 645,
    "preview": "import perfectLayout from '.';\n\n$.fn.perfectLayout = function(photos) {\n  const node = this;\n  const scrollBarSize = $('"
  },
  {
    "path": "lib/.tern-port",
    "chars": 5,
    "preview": "34063"
  },
  {
    "path": "lib/BSTLinearPartition.js",
    "chars": 1291,
    "preview": "export default function BSTLinearPartition(seq, k) {\n  if (seq.length <= 1) return [seq];\n  if (k >= seq.length) return "
  },
  {
    "path": "lib/BreakpointPartition.js",
    "chars": 2744,
    "preview": "// Rather than blindly perform a binary search from the maximum width. It starts\n// from the ideal width (The ideal widt"
  },
  {
    "path": "package.json",
    "chars": 1061,
    "preview": "{\n  \"name\": \"perfect-layout\",\n  \"version\": \"1.2.1\",\n  \"description\": \"Image layout generator based on linear partitionin"
  }
]

About this extraction

This page contains the full source code of the axyz/perfect-layout GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 12 files (24.2 KB), approximately 6.5k tokens, and a symbol index with 28 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.

Copied to clipboard!