landing-klikaset/wp-content/plugins/stratum/vendors/nested/jquery.nested.js

578 lines
19 KiB
JavaScript

/**
* jQuery Nested v1.03
*
* For a (total) gap free, multi column, grid layout experience.
* http://suprb.com/apps/nested/
* By Andreas Pihlström and additional brain activity by Jonas Blomdin
*
* Licensed under the MIT license.
*/
// Debouncing function from John Hann
// http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
// Copy pasted from http://paulirish.com/2009/throttled-smartresize-jquery-event-handler/
(function ($, sr) {
var debounce = function (func, threshold, execAsap) {
var timeout;
return function debounced() {
var obj = this,
args = arguments;
function delayed() {
if (!execAsap) func.apply(obj, args);
timeout = null;
};
if (timeout) clearTimeout(timeout);
else if (execAsap) func.apply(obj, args);
timeout = setTimeout(delayed, threshold || 150);
};
};
jQuery.fn[sr] = function (fn) {
return fn ? this.bind('resize', debounce(fn)) : this.trigger(sr);
};
})(jQuery, 'smartresize');
// Simple count object properties
if (!Object.keys) {
Object.keys = function (obj) {
var keys = [],
k;
for (k in obj) {
if (Object.prototype.hasOwnProperty.call(obj, k)) {
keys.push(k);
}
}
return keys;
};
}
// The Nested magic
(function ($) {
$.Nested = function (options, element) {
this.element = $(element);
this._init(options);
};
$.Nested.settings = {
selector: '.box',
minWidth: 50,
minColumns: 1,
gutter: 1,
centered: false,
resizeToFit: true, // will resize block bigger than the gap
resizeToFitOptions: {
resizeAny: true // will resize any block to fit the gap
},
animate: true,
animationOptions: {
speed: 20,
duration: 100,
queue: true,
complete: function () {}
}
};
$.Nested.prototype = {
_init: function (options) {
var container = this;
this.box = this.element;
$(this.box).css('position', 'relative');
this.options = $.extend(true, {}, $.Nested.settings, options);
this.elements = [];
this._isResizing = false;
this._update = true;
this.maxy = new Array();
// add smartresize
$(window).smartresize(function () {
container.resize();
});
// build box dimensions
this._setBoxes();
},
_setBoxes: function ($els, method) {
var self = this;
this.idCounter = 0;
this.counter = 0;
this.t = 0;
this.maxHeight = 0;
this.currWidth = 0;
this.total = this.box.find(this.options.selector);
this.matrix = {};
this.gridrow = new Object;
var calcWidth = !this.options.centered ? this.box.innerWidth() : $(window).width();
this.columns = Math.max(this.options.minColumns, parseInt(calcWidth / (this.options.minWidth + this.options.gutter)) + 1);
// build columns
var minWidth = this.options.minWidth;
var gutter = this.options.gutter;
var display = "block";
$els = this.box.find(this.options.selector);
$.each($els, function () {
var dim = parseInt($(this).attr('class').replace(/^.*size([0-9]+).*$/, '$1')).toString().split('');
var x = (dim[0] == "N") ? 1 : parseFloat(dim[0]);
var y = (dim[1] == "a") ? 1 : parseFloat(dim[1]);
var currWidth = minWidth * x + gutter * (x - 1);
var currHeight = minWidth * y + gutter * (y - 1);
$(this).css({
'display': display,
'position': 'absolute',
'width': currWidth,
'height': currHeight,
'top': $(this).position().top,
'left': $(this).position().left
}).removeClass('nested-moved').attr('data-box', self.idCounter).attr('data-width', currWidth);
self.idCounter++;
// render grid
self._renderGrid($(this), method);
});
// position grid
if (self.counter == self.total.length) {
// if option resizeToFit is true
if (self.options.resizeToFit) {
self.elements = self._fillGaps();
}
self._renderItems(self.elements);
// reset elements
self.elements = [];
}
},
_addMatrixRow: function (y) {
if (this.matrix[y]) {
return false;
} else this.matrix[y] = {};
for (var c = 0; c < (this.columns - 1); c++) {
var x = c * (this.options.minWidth + this.options.gutter);
this.matrix[y][x] = 'false';
}
},
_updateMatrix: function (el) {
var height = 0;
var t = parseInt(el['y']);
var l = parseInt(el['x']);
for (var h = 0; h < el['height']; h += (this.options.minWidth + this.options.gutter)) {
for (var w = 0; w < el['width']; w += (this.options.minWidth + this.options.gutter)) {
var x = l + w;
var y = t + h;
if (!this.matrix[y]) {
this._addMatrixRow(y);
}
this.matrix[y][x] = 'true';
}
}
},
_getObjectSize: function (obj) { // Helper to get size of object, should probably be moved
var size = 0;
$.each(obj, function (p, v) {
size++;
});
return size;
},
_fillGaps: function () {
var self = this;
var box = {};
$.each(this.elements, function (index, el) {
self._updateMatrix(el);
});
var arr = this.elements;
arr.sort(function (a, b) {
return a.y - b.y;
});
arr.reverse();
// Used to keep the highest y value for a box in memory
var topY = arr[0]['y'];
// Used for current y with added offset
var actualY = 0;
// Current number of rows in matrix
var rowsLeft = this._getObjectSize(this.matrix);
$.each(this.matrix, function (y, row) {
rowsLeft--;
actualY = parseInt(y); // + parseInt(self.box.offset().top);
$.each(row, function (x, col) {
if (col === 'false' && actualY < topY) {
if (!box.y) box.y = y;
if (!box.x) box.x = x;
if (!box.w) box.w = 0;
if (!box.h) box.h = self.options.minWidth;
box.w += (box.w) ? (self.options.minWidth + self.options.gutter) : self.options.minWidth;
var addonHeight = 0;
for (var row = 1; row < rowsLeft; row++) {
var z = parseInt(y) + parseInt(row * (self.options.minWidth + self.options.gutter));
if (self.matrix[z] && self.matrix[z][x] == 'false') {
addonHeight += (self.options.minWidth + self.options.gutter);
self.matrix[z][x] = 'true';
} else break;
}
box.h + (parseInt(addonHeight) / (self.options.minWidth + self.options.gutter) == rowsLeft) ? 0 : parseInt(addonHeight);
box.ready = true;
} else if (box.ready) {
$.each(arr, function (i, el) {
if (box.y <= arr[i]['y'] && (self.options.resizeToFitOptions.resizeAny || box.w <= arr[i]['width'] && box.h <= arr[i]['height'])) {
arr.splice(i, 1);
$(el['$el']).addClass('nested-moved');
self.elements.push({
$el: $(el['$el']),
x: parseInt(box.x),
y: parseInt(box.y),
col: i,
width: parseInt(box.w),
height: parseInt(box.h)
});
return false;
}
});
box = {};
}
});
});
self.elements = arr;
return self.elements;
},
_renderGrid: function ($box, method) {
this.counter++;
var ypos, gridy = ypos = 0;
var tot = 0;
var direction = !method ? "append" : "prepend";
// Width & height
var width = $box.width();
var height = $box.height();
// Calculate row and col
var col = Math.ceil(width / (this.options.minWidth + this.options.gutter));
var row = Math.ceil(height / (this.options.minWidth + this.options.gutter));
// lock widest box to match minColumns
if (col > this.options.minColumns) {
this.options.minColumns = col;
}
while (true) {
for (var y = col; y >= 0; y--) {
if (this.gridrow[gridy + y]) break;
this.gridrow[gridy + y] = new Object;
for (var x = 0; x < this.columns; x++) {
this.gridrow[gridy + y][x] = false;
}
}
for (var column = 0; column < (this.columns - col); column++) {
// Add default empty matrix, used to calculate and update matrix for each box
matrixY = gridy * (this.options.minWidth + this.options.gutter);
this._addMatrixRow(matrixY);
var fits = true;
for (var y = 0; y < row; y++) {
for (var x = 0; x < col; x++) {
if (!this.gridrow[gridy + y]) {
break;
}
if (this.gridrow[gridy + y][column + x]) {
fits = false;
break;
}
}
if (!fits) {
break;
}
}
if (fits) {
// Set as taken
for (var y = 0; y < row; y++) {
for (var x = 0; x < col; x++) {
if (!this.gridrow[gridy + y]) {
break;
}
this.gridrow[gridy + y][column + x] = true;
}
}
// Push to elements array
this._pushItem($box, column * (this.options.minWidth + this.options.gutter), gridy * (this.options.minWidth + this.options.gutter), width, height, col, row, direction);
return;
}
}
gridy++;
}
},
_pushItem: function ($el, x, y, w, h, cols, rows, method) {
if (method == "prepend") {
this.elements.unshift({
$el: $el,
x: x,
y: y,
width: w,
height: h,
cols: cols,
rows: rows
});
} else {
this.elements.push({
$el: $el,
x: x,
y: y,
width: w,
height: h,
cols: cols,
rows: rows
});
}
},
_setHeight: function ($els) {
var self = this;
$.each($els, function (index, value) {
// set maxHeight
var colY = (value['y'] + value['height']);
if (colY > self.maxHeight) {
self.maxHeight = colY;
}
});
return self.maxHeight;
},
_setWidth: function ($els) {
var self = this;
$.each($els, function (index, value) {
// set maxWidth
var colX = (value['x'] + value['width']);
if (colX > self.currWidth) {
self.currWidth = colX;
}
});
return self.currWidth;
},
_renderItems: function ($els) {
var self = this;
// set container height and width
this.box.css('height', this._setHeight($els));
if (this.options.centered) {
this.box.css({'width' : this._setWidth($els), 'margin-left' : 'auto', 'margin-right' : 'auto'});
}
$els.reverse();
var speed = this.options.animationOptions.speed;
var effect = this.options.animationOptions.effect;
var duration = this.options.animationOptions.duration;
var queue = this.options.animationOptions.queue;
var animate = this.options.animate;
var complete = this.options.animationOptions.complete;
var item = this;
var i = 0;
var t = 0;
$.each($els, function (index, value) {
$currLeft = $(value['$el']).position().left;
$currTop = $(value['$el']).position().top;
$currWidth = $(value['$el']).width();
$currHeight = $(value['$el']).width();
value['$el'].attr('data-y', $currTop).attr('data-x', $currLeft);
//if animate and queue
if (animate && queue && ($currLeft != value['x'] || $currTop != value['y'])) {
setTimeout(function () {
value['$el'].css({
'display': 'block',
'width': value['width'],
'height': value['height']
}).animate({
'left': value['x'],
'top': value['y']
}, duration);
t++;
if (t == i) {
complete.call(undefined, $els)
}
}, i * speed);
i++;
}
//if animate and no queue
if (animate && !queue && ($currLeft != value['x'] || $currTop != value['y'])) {
setTimeout(function () {
value['$el'].css({
'display': 'block',
'width': value['width'],
'height': value['height']
}).animate({
'left': value['x'],
'top': value['y']
}, duration);
t++;
if (t == i) {
complete.call(undefined, $els)
}
}, i);
i++;
}
//if no animation and no queue
if (!animate && ($currLeft != value['x'] || $currTop != value['y'])) {
value['$el'].css({
'display': 'block',
'width': value['width'],
'height': value['height'],
'left': value['x'],
'top': value['y']
});
t++;
if (t == i) {
complete.call(undefined, $els)
}
}
});
if (i == 0) {
complete.call(undefined, $els)
}
},
append: function ($els) {
this._isResizing = true;
this._setBoxes($els, 'append');
this._isResizing = false;
},
prepend: function ($els) {
this._isResizing = true;
this._setBoxes($els, 'prepend');
this._isResizing = false;
},
resize: function ($els) {
if (Object.keys(this.matrix[0]).length % Math.floor(this.element.width() / (this.options.minWidth + this.options.gutter)) > 0) {
this._isResizing = true;
this._setBoxes(this.box.find(this.options.selector));
this._isResizing = false;
}
},
refresh: function(options) {
options = options || this.options;
this.options = $.extend(true, {}, $.Nested.settings, options);
this.elements = [];
this._isResizing = false;
// build box dimensions
this._setBoxes();
},
destroy: function() {
var container = this;
$(window).unbind("resize", function () {
container.resize();
});
// unbind the resize event
$els = this.box.find(this.options.selector);
$($els).removeClass('nested-moved').removeAttr('style data-box data-width data-x data-y').removeData();
this.box.removeAttr("style").removeData();
}
}
var methods =
{
refresh: function(options) {
return this.each(function(){
var $this=$(this);
var nested = $this.data('nested');
nested.refresh(options);
});
},
destroy: function() {
return this.each(function(){
var $this=$(this);
var nested = $this.data('nested');
nested.destroy();
});
}
};
$.fn.nested = function (options, e) {
if(methods[options]) {
return methods[options].apply(this, Array.prototype.slice.call(arguments, 1));
}
if (typeof options === 'string') {
this.each(function () {
var container = $.data(this, 'nested');
container[options].apply(container, [e]);
});
} else {
this.each(function () {
$.data(this, 'nested', new $.Nested(options, this));
});
}
return this;
}
})(jQuery);