// created by Minh Nguyen; // version 1.0.6; (function($) { // for zeptojs; $.isNumeric == null && ($.isNumeric = function(src) { return src != null && src.constructor === Number; }); $.isFunction == null && ($.isFunction = function(src) { return src != null && src instanceof Function; }); var $W = $(window); var $D = $(document); var layoutManager = { // default setting; defaultConfig: { animate: false, cellW: 100, // function(container) {return 100;} cellH: 100, // function(container) {return 100;} delay: 0, // slowdown active block; engine: 'giot', fixSize: null, // resize + adjust = fill gap; //fixSize: 0, resize but keep ratio = no fill gap; //fixSize: 1, no resize + no adjust = no fill gap; gutterX: 15, // width spacing between blocks; gutterY: 15, // height spacing between blocks; keepOrder: false, selector: '> div', draggable: false, cacheSize: true, // caches the original size of block; rightToLeft: false, bottomToTop: false, onGapFound: function() {}, onComplete: function() {}, onResize: function() {}, onBlockDrag: function() {}, onBlockMove: function() {}, onBlockDrop: function() {}, onBlockReady: function() {}, onBlockFinish: function() {}, onBlockActive: function() {}, onBlockResize: function() {} }, plugin: {}, totalGrid: 1, transition: false, loadBlock: function(item, setting) { var runtime = setting.runtime; var gutterX = runtime.gutterX; var gutterY = runtime.gutterY; var cellH = runtime.cellH; var cellW = runtime.cellW; var block = null; var $item = $(item); var active = $item.data("active"); var fixPos = $item.attr('data-position'); var fixSize = parseInt($item.attr('data-fixSize')); var blockId = runtime.lastId++ + '-' + runtime.totalGrid; //ignore dragging block; if ($item.hasClass('fw-float')) return null; $item.attr({id: blockId, 'data-delay': item.index}); //remove animation for speed render; if (setting.animate && this.transition) { this.setTransition(item, ""); } isNaN(fixSize) && (fixSize = null); (fixSize == null) && (fixSize = setting.fixSize); var makeRound = (!fixSize) ? "round" : "ceil"; // store original size; $item.attr('data-height') == null && $item.attr('data-height', $item.height()); $item.attr('data-width') == null && $item.attr('data-width', $item.width()); var height = 1 * $item.attr('data-height'); var width = 1 * $item.attr('data-width'); if (!setting.cacheSize) { item.style.width = ""; width = $item.width(); item.style.height = ""; height = $item.height(); } var col = !width ? 0 : Math[makeRound]((width + gutterX) / cellW); var row = !height ? 0 : Math[makeRound]((height + gutterY) / cellH); // estimate size; if (!fixSize && setting.cellH == 'auto') { $item.width(cellW * col - gutterX); item.style.height = ""; height = $item.height(); row = !height ? 0 : Math.round((height + gutterY) / cellH); } if (!fixSize && setting.cellW == 'auto') { $item.height(cellH * row - gutterY); item.style.width = ""; width = $item.width(); col = !width ? 0 : Math.round((width + gutterX) / cellW); } // for none resize block; if ((fixSize != null) && (col > runtime.limitCol || row > runtime.limitRow)) { block = null; } else { // get smallest width and smallest height of block; // using for image runtime; row && row < runtime.minHoB && (runtime.minHoB = row); col && col < runtime.minWoB && (runtime.minWoB = col); // get biggest width and biggest height of block; row > runtime.maxHoB && (runtime.maxHoB = row); col > runtime.maxWoB && (runtime.maxWoB = col); width == 0 && (col = 0); height == 0 && (row = 0); block = { resize: false, id: blockId, width: col, height: row, fixSize: fixSize }; // for fix position; if (fixPos) { fixPos = fixPos.split("-"); block.y = 1 * fixPos[0]; block.x = 1 * fixPos[1]; block.width = fixSize != null ? col : Math.min(col, runtime.limitCol - block.x); block.height = fixSize != null ? row : Math.min(row, runtime.limitRow - block.y); var holeId = block.y + "-" + block.x + "-" + block.width + "-" + block.height; if (active) { runtime.holes[holeId] = { id: block.id, top: block.y, left: block.x, width: block.width, height: block.height }; this.setBlock(block, setting); } else { delete runtime.holes[holeId]; } } } // for css animation; if ($item.attr("data-state") == null) { $item.attr("data-state", "init"); } else { $item.attr("data-state", "move"); } setting.onBlockReady.call(item, block, setting); return (fixPos && active) ? null : block; }, setBlock: function(block, setting) { var runtime = setting.runtime; var gutterX = runtime.gutterX; var gutterY = runtime.gutterY; var height = block.height; var width = block.width; var cellH = runtime.cellH; var cellW = runtime.cellW; var x = block.x; var y = block.y; if (setting.rightToLeft) { x = runtime.limitCol - x - width; } if (setting.bottomToTop) { y = runtime.limitRow - y - height; } var realBlock = { fixSize: block.fixSize, resize: block.resize, top: y * cellH, left: x * cellW, width: cellW * width - gutterX, height: cellH * height - gutterY }; realBlock.top = 1 * realBlock.top.toFixed(2); realBlock.left = 1 * realBlock.left.toFixed(2); realBlock.width = 1 * realBlock.width.toFixed(2); realBlock.height = 1 * realBlock.height.toFixed(2); //runtime.length += 1; block.id && (runtime.blocks[block.id] = realBlock); // for append feature; return realBlock; }, showBlock: function(item, setting) { var runtime = setting.runtime; var method = setting.animate && !this.transition ? 'animate' : 'css'; var block = runtime.blocks[item.id]; var $item = $(item); var self = this; var start = $item.attr("data-state") != "move"; var trans = start ? "width 0.5s, height 0.5s" : "top 0.5s, left 0.5s, width 0.5s, height 0.5s, opacity 0.5s"; item.delay && clearTimeout(item.delay); //ignore dragging block; if ($item.hasClass('fw-float')) return; // kill the old transition; self.setTransition(item, ""); item.style.position = "absolute"; setting.onBlockActive.call(item, block, setting); function action() { // start to arrange; start && $item.attr("data-state", "start"); // add animation by using css3 transition; if (setting.animate && self.transition) { self.setTransition(item, trans); } runtime.length -= 1; // for hidden block; if (!block) { //var position = $item.position(); <= make speed so slow; var height = parseInt(item.style.height) || 0; var width = parseInt(item.style.width) || 0; var left = parseInt(item.style.left) || 0; var top = parseInt(item.style.top) || 0; $item[method]({ left: left + width / 2, top: top + height / 2, width: 0, height: 0, opacity: 0 }); } else { if (block.fixSize) { block.height = 1 * $item.attr("data-height"); block.width = 1 * $item.attr("data-width"); } $item["css"]({ opacity: 1, width: block.width, height: block.height }); // for animating by javascript; $item[method]({ top: block.top, left: block.left }); if ($item.attr('data-nested') != null) { self.nestedGrid(item, setting); } } setting.onBlockFinish.call(item, block, setting); if (runtime.length == 0) { var duration = setting.animate ? 500 : 0; item.delay = setTimeout(function() { setting.onComplete.call(item, block, setting); }, duration); } } block && block.resize && setting.onBlockResize.call(item, block, setting); setting.delay > 0 ? (item.delay = setTimeout(action, setting.delay * $item.attr("data-delay"))) : action(); }, nestedGrid: function(item, setting) { var innerWall, $item = $(item), runtime = setting.runtime; var gutterX = $item.attr("data-gutterX") || setting.gutterX; var gutterY = $item.attr("data-gutterY") || setting.gutterY; var method = $item.attr("data-method") || "fitZone"; var nested = $item.attr('data-nested') || "> div"; var cellH = $item.attr("data-cellH") || setting.cellH; var cellW = $item.attr("data-cellW") || setting.cellW; var block = runtime.blocks[item.id]; if (block) { innerWall = new Freewall($item); innerWall.reset({ cellH: cellH, cellW: cellW, gutterX: 1 * gutterX, gutterY: 1 * gutterY, selector: nested, cacheSize: false }); switch (method) { case "fitHeight": innerWall[method](block.height); break; case "fitWidth": innerWall[method](block.width); break; case "fitZone": innerWall[method](block.width, block.height); break; } } }, adjustBlock: function(block, setting) { var runtime = setting.runtime; var gutterX = runtime.gutterX; var gutterY = runtime.gutterY; var $item = $("#" + block.id); var cellH = runtime.cellH; var cellW = runtime.cellW; if (setting.cellH == 'auto') { $item.width(block.width * cellW - gutterX); $item[0].style.height = ""; block.height = Math.round(($item.height() + gutterY) / cellH); } }, adjustUnit: function(width, height, setting) { var gutterX = setting.gutterX; var gutterY = setting.gutterY; var runtime = setting.runtime; var cellW = setting.cellW; var cellH = setting.cellH; $.isFunction(cellW) && (cellW = cellW(width)); cellW = 1 * cellW; !$.isNumeric(cellW) && (cellW = 1); $.isFunction(cellH) && (cellH = cellH(height)); cellH = 1 * cellH; !$.isNumeric(cellH) && (cellH = 1); if ($.isNumeric(width)) { // adjust cell width via container; cellW < 1 && (cellW = cellW * width); // estimate total columns; var limitCol = Math.max(1, Math.floor(width / cellW)); // adjust unit size for fit width; if (!$.isNumeric(gutterX)) { gutterX = (width - limitCol * cellW) / Math.max(1, (limitCol - 1)); gutterX = Math.max(0, gutterX); } limitCol = Math.floor((width + gutterX) / cellW); runtime.cellW = (width + gutterX) / Math.max(limitCol, 1); runtime.cellS = runtime.cellW / cellW; runtime.gutterX = gutterX; runtime.limitCol = limitCol; } if ($.isNumeric(height)) { // adjust cell height via container; cellH < 1 && (cellH = cellH * height); // estimate total rows; var limitRow = Math.max(1, Math.floor(height / cellH)); // adjust size unit for fit height; if (!$.isNumeric(gutterY)) { gutterY = (height - limitRow * cellH) / Math.max(1, (limitRow - 1)); gutterY = Math.max(0, gutterY); } limitRow = Math.floor((height + gutterY) / cellH); runtime.cellH = (height + gutterY) / Math.max(limitRow, 1); runtime.cellS = runtime.cellH / cellH; runtime.gutterY = gutterY; runtime.limitRow = limitRow; } if (!$.isNumeric(width)) { // adjust cell width via cell height; cellW < 1 && (cellW = runtime.cellH); runtime.cellW = cellW != 1 ? cellW * runtime.cellS : 1; runtime.gutterX = gutterX; runtime.limitCol = 666666; } if (!$.isNumeric(height)) { // adjust cell height via cell width; cellH < 1 && (cellH = runtime.cellW); runtime.cellH = cellH != 1 ? cellH * runtime.cellS : 1; runtime.gutterY = gutterY; runtime.limitRow = 666666; } runtime.keepOrder = setting.keepOrder; }, resetGrid: function(runtime) { runtime.blocks = {}; runtime.length = 0; runtime.cellH = 0; runtime.cellW = 0; runtime.lastId = 1; runtime.matrix = {}; runtime.totalCol = 0; runtime.totalRow = 0; }, setDraggable: function(item, option) { var isTouch = false; var config = { startX: 0, //start clientX; startY: 0, top: 0, left: 0, handle: null, onDrop: function() {}, onDrag: function() {}, onStart: function() {} }; $(item).each(function() { var setting = $.extend({}, config, option); var handle = setting.handle || this; var ele = this; var $E = $(ele); var $H = $(handle); var posStyle = $E.css("position"); posStyle != "absolute" && $E.css("position", "relative"); function mouseDown(evt) { evt.stopPropagation(); evt = evt.originalEvent; if (evt.touches) { isTouch = true; evt = evt.changedTouches[0]; } if (evt.button != 2 && evt.which != 3) { setting.onStart.call(ele, evt); setting.startX = evt.clientX; setting.startY = evt.clientY; setting.top = parseInt($E.css("top")) || 0; setting.left = parseInt($E.css("left")) || 0; $D.bind("mouseup touchend", mouseUp); $D.bind("mousemove touchmove", mouseMove); } return false; }; function mouseMove(evt) { evt = evt.originalEvent; isTouch && (evt = evt.changedTouches[0]); $E.css({ top: setting.top - (setting.startY - evt.clientY), left: setting.left - (setting.startX - evt.clientX) }); setting.onDrag.call(ele, evt); }; function mouseUp(evt) { evt = evt.originalEvent; isTouch && (evt = evt.changedTouches[0]); setting.onDrop.call(ele, evt); $D.unbind("mouseup touchend", mouseUp); $D.unbind("mousemove touchmove", mouseMove); }; // ignore drag drop on text field; $E.find("iframe, form, input, textarea, .ignore-drag") .each(function() { $(this).on("touchstart mousedown", function(evt) { evt.stopPropagation(); }); }); $D.unbind("mouseup touchend", mouseUp); $D.unbind("mousemove touchmove", mouseMove); $H.unbind("mousedown touchstart").bind("mousedown touchstart", mouseDown); }); }, setTransition: function(item, trans) { var style = item.style; var $item = $(item); // remove animation; if (!this.transition && $item.stop) { $item.stop(); } else if (style.webkitTransition != null) { style.webkitTransition = trans; } else if (style.MozTransition != null) { style.MozTransition = trans; } else if (style.msTransition != null) { style.msTransition = trans; } else if (style.OTransition != null) { style.OTransition = trans; } else { style.transition = trans; } }, getFreeArea: function(t, l, runtime) { var maxY = Math.min(t + runtime.maxHoB, runtime.limitRow); var maxX = Math.min(l + runtime.maxWoB, runtime.limitCol); var minX = maxX; var minY = maxY; var matrix = runtime.matrix; // find limit zone by horizon; for (var y = t; y < minY; ++y) { for (var x = l; x < maxX; ++x) { if (matrix[y + '-' + x]) { (l < x && x < minX) && (minX = x); } } } // find limit zone by vertical; for (var y = t; y < maxY; ++y) { for (var x = l; x < minX; ++x) { if (matrix[y + '-' + x]) { (t < y && y < minY) && (minY = y); } } } return { top: t, left: l, width: minX - l, height: minY - t }; }, setWallSize: function(runtime, container) { var totalRow = runtime.totalRow; var totalCol = runtime.totalCol; var gutterY = runtime.gutterY; var gutterX = runtime.gutterX; var cellH = runtime.cellH; var cellW = runtime.cellW; var totalWidth = Math.max(0, cellW * totalCol - gutterX); var totalHeight = Math.max(0, cellH * totalRow - gutterY); container.attr({ 'data-total-col': totalCol, 'data-total-row': totalRow, 'data-wall-width': Math.ceil(totalWidth), 'data-wall-height': Math.ceil(totalHeight) }); if (runtime.limitCol < runtime.limitRow) { // do not set height with nesting grid; !container.attr("data-height") && container.height(Math.ceil(totalHeight)); } } }; var engine = { // Giot just a person name; giot: function(items, setting) { var runtime = setting.runtime, row = runtime.limitRow, col = runtime.limitCol, x = 0, y = 0, maxX = runtime.totalCol, maxY = runtime.totalRow, wall = {}, holes = runtime.holes, block = null, matrix = runtime.matrix, bigLoop = Math.max(col, row), freeArea = null, misBlock = null, fitWidth = col < row ? 1 : 0, lastBlock = null, smallLoop = Math.min(col, row); // fill area with top, left, width, height; function fillMatrix(id, t, l, w, h) { for (var y = t; y < t + h;) { for (var x = l; x < l + w;) { matrix[y + '-' + x] = id; ++x > maxX && (maxX = x); } ++y > maxY && (maxY = y); } } // set holes on the wall; for (var i in holes) { if (holes.hasOwnProperty(i)) { fillMatrix(holes[i]["id"] || true, holes[i]['top'], holes[i]['left'], holes[i]['width'], holes[i]['height']); } } for (var b = 0; b < bigLoop; ++b) { if (!items.length) break; fitWidth ? (y = b) : (x = b); lastBlock = null; for (var s = 0; s < smallLoop; ++s) { if (!items.length) break; block = null; fitWidth ? (x = s) : (y = s); if (runtime.matrix[y + '-' + x]) continue; freeArea = layoutManager.getFreeArea(y, x, runtime); // trying resize last block to fit free area; if (setting.fixSize == null) { // resize near block to fill gap; if (lastBlock && !fitWidth && runtime.minHoB > freeArea.height) { lastBlock.height += freeArea.height; lastBlock.resize = true; fillMatrix(lastBlock.id, lastBlock.y, lastBlock.x, lastBlock.width, lastBlock.height); layoutManager.setBlock(lastBlock, setting); continue; } else if (lastBlock && fitWidth && runtime.minWoB > freeArea.width) { lastBlock.width += freeArea.width; lastBlock.resize = true; fillMatrix(lastBlock.id, lastBlock.y, lastBlock.x, lastBlock.width, lastBlock.height); layoutManager.setBlock(lastBlock, setting); continue; } } // get the next block to keep order; if (runtime.keepOrder) { block = items.shift(); block.resize = true; } else { // find a suitable block to fit gap; for (var i = 0; i < items.length; ++i) { if (items[i].height > freeArea.height) continue; if (items[i].width > freeArea.width) continue; block = items.splice(i, 1)[0]; break; } // trying resize the other block to fit gap; if (block == null && setting.fixSize == null) { // get other block fill to gap; for (var i = 0; i < items.length; ++i) { if (items[i]['fixSize'] != null) continue; block = items.splice(i, 1)[0]; block.resize = true; break; } } } if (block != null) { // resize block with free area; if (block.resize) { if (fitWidth) { block.width = freeArea.width; if (setting.cellH == 'auto') { layoutManager.adjustBlock(block, setting); } // for fitZone; block.height = Math.min(block.height, freeArea.height); } else { block.height = freeArea.height; // for fitZone; block.width = Math.min(block.width, freeArea.width); } } wall[block.id] = { id: block.id, x: x, y: y, width: block.width, height: block.height, resize: block.resize, fixSize: block.fixSize }; // keep success block for next round; lastBlock = wall[block.id]; fillMatrix(lastBlock.id, lastBlock.y, lastBlock.x, lastBlock.width, lastBlock.height); layoutManager.setBlock(lastBlock, setting); } else { // get expect area; var misBlock = { x: x, y: y, fixSize: 0 }; if (fitWidth) { misBlock.width = freeArea.width; misBlock.height = 0; var lastX = x - 1; var lastY = y; while (matrix[lastY + '-' + lastX]) { matrix[lastY + '-' + x] = true; misBlock.height += 1; lastY += 1; } } else { misBlock.height = freeArea.height; misBlock.width = 0; var lastY = y - 1; var lastX = x; while (matrix[lastY + '-' + lastX]) { matrix[y + '-' + lastX] = true; misBlock.width += 1; lastX += 1; } } setting.onGapFound(layoutManager.setBlock(misBlock, setting), setting); } } } runtime.matrix = matrix; runtime.totalRow = maxY; runtime.totalCol = maxX; } }; function Freewall(selector) { var container = $(selector); if (container.css('position') == 'static') { container.css('position', 'relative'); } var MAX = Number.MAX_VALUE; var klass = this; // increase the instance index; layoutManager.totalGrid += 1; var setting = $.extend({}, layoutManager.defaultConfig); var runtime = { arguments: null, blocks: {}, // store all items; events: {}, // store custome events; matrix: {}, holes: {}, // forbidden zone; cellW: 0, cellH: 0, // unit adjust; cellS: 1, // unit scale; filter: '', // filter selector; lastId: 0, length: 0, maxWoB: 0, // max width of block; maxHoB: 0, minWoB: MAX, minHoB: MAX, // min height of block; running: 0, // flag to check layout arranging; gutterX: 15, gutterY: 15, totalCol: 0, totalRow: 0, limitCol: 666666, // maximum column; limitRow: 666666, sortFunc: null, keepOrder: false }; setting.runtime = runtime; runtime.totalGrid = layoutManager.totalGrid; // check browser support transition; var bodyStyle = document.body.style; if (!layoutManager.transition) { (bodyStyle.webkitTransition != null || bodyStyle.MozTransition != null || bodyStyle.msTransition != null || bodyStyle.OTransition != null || bodyStyle.transition != null) && (layoutManager.transition = true); } function setDraggable(item) { var gutterX = runtime.gutterX; var gutterY = runtime.gutterY; var cellH = runtime.cellH; var cellW = runtime.cellW; var $item = $(item); var handle = $item.find($item.attr("data-handle")); layoutManager.setDraggable(item, { handle: handle[0], onStart: function(event) { if (setting.animate && layoutManager.transition) { layoutManager.setTransition(this, ""); } $item.css('z-index', 9999).addClass('fw-float'); setting.onBlockDrag.call(item, event); }, onDrag: function(event, tracker) { var position = $item.position(); var top = Math.round(position.top / cellH); var left = Math.round(position.left / cellW); var width = Math.round($item.width() / cellW); var height = Math.round($item.height() / cellH); top = Math.min(Math.max(0, top), runtime.limitRow - height); left = Math.min(Math.max(0, left), runtime.limitCol - width); klass.setHoles({top: top, left: left, width: width, height: height}); klass.refresh(); setting.onBlockMove.call(item, event); }, onDrop: function(event) { var position = $item.position(); var top = Math.round(position.top / cellH); var left = Math.round(position.left / cellW); var width = Math.round($item.width() / cellW); var height = Math.round($item.height() / cellH); top = Math.min(Math.max(0, top), runtime.limitRow - height); left = Math.min(Math.max(0, left), runtime.limitCol - width); $item.removeClass('fw-float'); $item.css({ zIndex: "auto", top: top * cellH, left: left * cellW }); //check old drag element; var x, y, key, oldDropId; for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { key = (y + top) + "-" + (x + left); oldDropId = runtime.matrix[key]; if (oldDropId && oldDropId != true) { $("#" + oldDropId).removeAttr("data-position"); } } } runtime.holes = {}; $item.attr({ "data-width": $item.width(), "data-height": $item.height(), "data-position": top + "-" + left }); klass.refresh(); setting.onBlockDrop.call(item, event); } }); } $.extend(klass, { addCustomEvent: function(name, func) { var events = runtime.events; name = name.toLowerCase(); !events[name] && (events[name] = []); func.eid = events[name].length; events[name].push(func); return this; }, appendBlock: function(items) { var allBlock = $(items).appendTo(container); var block = null; var activeBlock = []; if (runtime.arguments) { if ($.isFunction(runtime.sortFunc)) { allBlock.sort(runtime.sortFunc); } allBlock.each(function(index, item) { item.index = ++index; block = layoutManager.loadBlock(item, setting); block && activeBlock.push(block); }); engine[setting.engine](activeBlock, setting); layoutManager.setWallSize(runtime, container); runtime.length = allBlock.length; allBlock.each(function(index, item) { layoutManager.showBlock(item, setting); if (setting.draggable || item.getAttribute('data-draggable')) { setDraggable(item); } }); } }, /* add one or more blank area (hole) on layout; example: wall.appendHoles({ top: 10, left: 36, width: 2, height: 6 }); wall.appendHoles([ { top: 16, left: 16, width: 8, height: 2 }, { top: 10, left: 36, width: 2, height: 6 } ]); */ appendHoles: function(holes) { var newHoles = [].concat(holes), h = {}, i; for (i = 0; i < newHoles.length; ++i) { h = newHoles[i]; runtime.holes[h.top + "-" + h.left + "-" + h.width + "-" + h.height] = h; } return this; }, container: container, destroy: function() { var allBlock = container.find(setting.selector).removeAttr('id'), block = null, activeBlock = []; allBlock.each(function(index, item) { $item = $(item); var width = 1 * $item.attr('data-width') || ""; var height = 1 * $item.attr('data-height') || ""; $item.width(width).height(height).css({ position: 'static' }); }); }, fillHoles: function(holes) { if (arguments.length == 0) { runtime.holes = {}; } else { var newHoles = [].concat(holes), h = {}, i; for (i = 0; i < newHoles.length; ++i) { h = newHoles[i]; delete runtime.holes[h.top + "-" + h.left + "-" + h.width + "-" + h.height]; } } return this; }, filter: function(filter) { runtime.filter = filter; if (runtime.arguments) { this.refresh(); } return this; }, fireEvent: function(name, object, setting) { var events = runtime.events; name = name.toLowerCase(); if (events[name] && events[name].length) { for (var i = 0; i < events[name].length; ++i) { events[name][i].call(this, object, setting); } } return this; }, fitHeight: function(height) { var height = height ? height : container.height() || $W.height(); this.fitZone('auto', height); runtime.arguments = arguments; }, fitWidth: function(width) { var width = width ? width : container.width() || $W.width(); this.fitZone(width, 'auto'); runtime.arguments = arguments; }, fitZone: function(width, height) { var allBlock = container.find(setting.selector).removeAttr('id'), block = null, activeBlock = []; height = height ? height : container.height() || $W.height(); width = width ? width : container.width() || $W.width(); runtime.arguments = arguments; layoutManager.resetGrid(runtime); layoutManager.adjustUnit(width, height, setting); if (runtime.filter) { allBlock.data('active', 0); allBlock.filter(runtime.filter).data('active', 1); } else { allBlock.data('active', 1); } if ($.isFunction(runtime.sortFunc)) { allBlock.sort(runtime.sortFunc); } allBlock.each(function(index, item) { var $item = $(item); item.index = ++index; block = layoutManager.loadBlock(item, setting); block && $item.data("active") && activeBlock.push(block); }); klass.fireEvent('onGridReady', container, setting); engine[setting.engine](activeBlock, setting); layoutManager.setWallSize(runtime, container); klass.fireEvent('onGridArrange', container, setting); runtime.length = allBlock.length; allBlock.each(function(index, item) { layoutManager.showBlock(item, setting); if (setting.draggable || item.getAttribute('data-draggable')) { setDraggable(item); } }); }, /* set block with special position, the top and left are multiple of unit width/height; example: wall.fixPos({ top: 0, left: 0, block: $('.free') }); */ fixPos: function(option) { $(option.block).attr({'data-position': option.top + "-" + option.left}); return this; }, /* set block with special size, the width and height are multiple of unit width/height; example: wall.fixSize({ height: 5, width: 2, block: $('.free') }); */ fixSize: function(option) { option.height != null && $(option.block).attr({'data-height': option.height}); option.width != null && $(option.block).attr({'data-width': option.width}); return this; }, prepend: function(items) { container.prepend(items); if (runtime.arguments) { this.refresh(); } return this; }, refresh: function() { var params = arguments.length ? arguments : runtime.arguments; var oldArg = runtime.arguments; var method = oldArg ? oldArg.callee : this.fitWidth; method.apply(this, Array.prototype.slice.call(params, 0)); return this; }, /* custom layout setting; example: wall.reset({ selector: '.brick', animate: true, cellW: 160, cellH: 160, delay: 50, onResize: function() { wall.fitWidth(); } }); */ reset: function(option) { $.extend(setting, option); return this; }, /* create one or more blank area (hole) on layout; example: wall.setHoles({ top: 2, left: 2, width: 2, height: 2 }); */ setHoles: function(holes) { var newHoles = [].concat(holes), h = {}, i; runtime.holes = {}; for (i = 0; i < newHoles.length; ++i) { h = newHoles[i]; runtime.holes[h.top + "-" + h.left + "-" + h.width + "-" + h.height] = h; } return this; }, /* sort items by using array sort function; example: wall.sortBy(function(itemA, itemB) { return $(itemA).width() - $(itemB).width(); }); */ sortBy: function(func) { runtime.sortFunc = func; if (runtime.arguments) { this.refresh(); } return this; }, unFilter: function() { delete runtime.filter; this.refresh(); return this; } }); container.attr('data-min-width', Math.floor($W.width() / 80) * 80); // execute plugins; for (var i in layoutManager.plugin) { if (layoutManager.plugin.hasOwnProperty(i)) { layoutManager.plugin[i].call(klass, setting, container); } } // setup resize event; $W.resize(function() { if (runtime.running) return; runtime.running = 1; setTimeout(function() { runtime.running = 0; setting.onResize.call(klass, container); }, 122); container.attr('data-min-width', Math.floor($W.width() / 80) * 80); }); }; /* add default setting; example: Freewall.addConfig({ offsetLeft: 0 }); */ Freewall.addConfig = function(newConfig) { // add default setting; $.extend(layoutManager.defaultConfig, newConfig); }; /* support create new arrange algorithm; example: Freewall.createEngine({ slice: function(items, setting) { // slice engine; } }); */ Freewall.createEngine = function(engineData) { // create new engine; $.extend(engine, engineData); }; /* support create new plugin; example: Freewall.createPlugin({ centering: function(setting, container) { console.log(this); console.log(setting); } })l */ Freewall.createPlugin = function(pluginData) { // register new plugin; $.extend(layoutManager.plugin, pluginData); }; /* support access helper function; example: Freewall.getMethod('setBlock')(block, setting); */ Freewall.getMethod = function(method) { // get helper method; return layoutManager[method]; }; window.Freewall = window.freewall = Freewall; })(window.Zepto || window.jQuery);