1 // created by Minh Nguyen;
\r
7 $.isNumeric == null && ($.isNumeric = function(src) {
\r
8 return src != null && src.constructor === Number;
\r
11 $.isFunction == null && ($.isFunction = function(src) {
\r
12 return src != null && src instanceof Function;
\r
16 var $D = $(document);
\r
18 var layoutManager = {
\r
22 cellW: 100, // function(container) {return 100;}
\r
23 cellH: 100, // function(container) {return 100;}
\r
24 delay: 0, // slowdown active block;
\r
25 engine: 'giot', // 'giot' is a person name;
\r
26 fixSize: null, // resize + adjust = fill gap;
\r
27 //fixSize: 0, resize but keep ratio = no fill gap;
\r
28 //fixSize: 1, no resize + no adjust = no fill gap;
\r
29 gutterX: 15, // width spacing between blocks;
\r
30 gutterY: 15, // height spacing between blocks;
\r
34 cacheSize: true, // caches the original size of block;
\r
37 onGapFound: function() {},
\r
38 onComplete: function() {},
\r
39 onResize: function() {},
\r
40 onBlockDrag: function() {},
\r
41 onBlockMove: function() {},
\r
42 onBlockDrop: function() {},
\r
43 onBlockReady: function() {},
\r
44 onBlockFinish: function() {},
\r
45 onBlockActive: function() {},
\r
46 onBlockResize: function() {}
\r
51 loadBlock: function(item, setting) {
\r
52 var runtime = setting.runtime;
\r
53 var gutterX = runtime.gutterX;
\r
54 var gutterY = runtime.gutterY;
\r
55 var cellH = runtime.cellH;
\r
56 var cellW = runtime.cellW;
\r
58 var $item = $(item);
\r
59 var active = $item.data("active");
\r
60 var fixPos = $item.attr('data-position');
\r
61 var fixSize = parseInt($item.attr('data-fixSize'));
\r
62 var blockId = runtime.lastId++ + '-' + runtime.totalGrid;
\r
64 //ignore dragging block;
\r
65 if ($item.hasClass('fw-float')) return;
\r
66 $item.attr({id: blockId, 'data-delay': item.index});
\r
68 //remove animation for speed render;
\r
69 if (setting.animate && this.transition) {
\r
70 this.setTransition(item, "");
\r
73 isNaN(fixSize) && (fixSize = null);
\r
74 (fixSize == null) && (fixSize = setting.fixSize);
\r
75 var makeRound = (fixSize >= 1) ? "ceil" : "round";
\r
76 // store original size;
\r
78 $item.attr('data-height') == null && $item.attr('data-height', $item.height());
\r
79 $item.attr('data-width') == null && $item.attr('data-width', $item.width());
\r
80 var height = 1 * $item.attr('data-height');
\r
81 var width = 1 * $item.attr('data-width');
\r
83 if (!setting.cacheSize) {
\r
84 item.style.width = "";
\r
85 width = $item.width();
\r
87 item.style.height = "";
\r
88 height = $item.height();
\r
91 var col = !width ? 0 : Math[makeRound]((width + gutterX) / cellW);
\r
92 var row = !height ? 0 : Math[makeRound]((height + gutterY) / cellH);
\r
95 if (!fixSize && setting.cellH == 'auto') {
\r
96 $item.width(cellW * col - gutterX);
\r
97 item.style.height = "";
\r
98 height = $item.height();
\r
99 row = !height ? 0 : Math.round((height + gutterY) / cellH);
\r
102 if (!fixSize && setting.cellW == 'auto') {
\r
103 $item.height(cellH * row - gutterY);
\r
104 item.style.width = "";
\r
105 width = $item.width();
\r
106 col = !width ? 0 : Math.round((width + gutterX) / cellW);
\r
109 // for none resize block;
\r
110 if ((fixSize != null) && (col > runtime.limitCol || row > runtime.limitRow)) {
\r
113 // get smallest width and smallest height of block;
\r
114 // using for image runtime;
\r
115 row && row < runtime.minHoB && (runtime.minHoB = row);
\r
116 col && col < runtime.minWoB && (runtime.minWoB = col);
\r
118 // get biggest width and biggest height of block;
\r
119 row > runtime.maxHoB && (runtime.maxHoB = row);
\r
120 col > runtime.maxWoB && (runtime.maxWoB = col);
\r
122 width == 0 && (col = 0);
\r
123 height == 0 && (row = 0);
\r
133 // for fix position;
\r
135 fixPos = fixPos.split("-");
\r
136 block.y = 1 * fixPos[0];
\r
137 block.x = 1 * fixPos[1];
\r
138 block.width = fixSize != null ? col : Math.min(col, runtime.limitCol - block.x);
\r
139 block.height = fixSize != null ? row : Math.min(row, runtime.limitRow - block.y);
\r
140 var holeId = block.y + "-" + block.x + "-" + block.width + "-" + block.height;
\r
142 runtime.holes[holeId] = {
\r
146 width: block.width,
\r
147 height: block.height
\r
149 this.setBlock(block, setting);
\r
151 delete runtime.holes[holeId];
\r
157 // for css animation;
\r
158 if ($item.attr("data-state") == null) {
\r
159 $item.attr("data-state", "init");
\r
161 $item.attr("data-state", "move");
\r
164 setting.onBlockReady.call(item, block, setting);
\r
166 return (fixPos && active) ? null : block;
\r
168 setBlock: function(block, setting) {
\r
169 var runtime = setting.runtime;
\r
170 var gutterX = runtime.gutterX;
\r
171 var gutterY = runtime.gutterY;
\r
172 var height = block.height;
\r
173 var width = block.width;
\r
174 var cellH = runtime.cellH;
\r
175 var cellW = runtime.cellW;
\r
179 if (setting.rightToLeft) {
\r
180 x = runtime.limitCol - x - width;
\r
182 if (setting.bottomToTop) {
\r
183 y = runtime.limitRow - y - height;
\r
187 fixSize: block.fixSize,
\r
188 resize: block.resize,
\r
191 width: cellW * width - gutterX,
\r
192 height: cellH * height - gutterY
\r
195 realBlock.top = 1 * realBlock.top.toFixed(2);
\r
196 realBlock.left = 1 * realBlock.left.toFixed(2);
\r
197 realBlock.width = 1 * realBlock.width.toFixed(2);
\r
198 realBlock.height = 1 * realBlock.height.toFixed(2);
\r
200 //runtime.length += 1;
\r
201 block.id && (runtime.blocks[block.id] = realBlock);
\r
203 // for append feature;
\r
206 showBlock: function(item, setting) {
\r
207 var runtime = setting.runtime;
\r
208 var method = setting.animate && !this.transition ? 'animate' : 'css';
\r
209 var block = runtime.blocks[item.id];
\r
210 var $item = $(item);
\r
212 var start = $item.attr("data-state") != "move";
\r
213 var trans = start ? "width 0.5s, height 0.5s" : "top 0.5s, left 0.5s, width 0.5s, height 0.5s, opacity 0.5s";
\r
215 item.delay && clearTimeout(item.delay);
\r
216 //ignore dragging block;
\r
217 if ($item.hasClass('fw-float')) return;
\r
219 // kill the old transition;
\r
220 self.setTransition(item, "");
\r
221 item.style.position = "absolute";
\r
222 setting.onBlockActive.call(item, block, setting);
\r
224 function action() {
\r
225 // start to arrange;
\r
226 start && $item.attr("data-state", "start");
\r
227 // add animation by using css3 transition;
\r
228 if (setting.animate && self.transition) {
\r
229 self.setTransition(item, trans);
\r
232 // for hidden block;
\r
234 //var position = $item.position(); <= make speed so slow;
\r
235 var height = parseInt(item.style.height) || 0;
\r
236 var width = parseInt(item.style.width) || 0;
\r
237 var left = parseInt(item.style.left) || 0;
\r
238 var top = parseInt(item.style.top) || 0;
\r
240 left: left + width / 2,
\r
241 top: top + height / 2,
\r
247 if (block.fixSize) {
\r
248 block.height = 1 * $item.attr("data-height");
\r
249 block.width = 1 * $item.attr("data-width");
\r
254 width: block.width,
\r
255 height: block.height
\r
258 // for animating by javascript;
\r
264 if ($item.attr('data-nested') != null) {
\r
265 self.nestedGrid(item, setting);
\r
269 runtime.length -= 1;
\r
271 setting.onBlockFinish.call(item, block, setting);
\r
273 runtime.length == 0 && setting.onComplete.call(item, block, setting);
\r
276 block && block.resize && setting.onBlockResize.call(item, block, setting);
\r
278 setting.delay > 0 ? (item.delay = setTimeout(action, setting.delay * $item.attr("data-delay"))) : action();
\r
280 nestedGrid: function(item, setting) {
\r
281 var innerWall, $item = $(item), runtime = setting.runtime;
\r
282 var gutterX = $item.attr("data-gutterX") || setting.gutterX;
\r
283 var gutterY = $item.attr("data-gutterY") || setting.gutterY;
\r
284 var method = $item.attr("data-method") || "fitZone";
\r
285 var nested = $item.attr('data-nested') || "> div";
\r
286 var cellH = $item.attr("data-cellH") || setting.cellH;
\r
287 var cellW = $item.attr("data-cellW") || setting.cellW;
\r
288 var block = runtime.blocks[item.id];
\r
291 innerWall = new freewall($item);
\r
295 gutterX: 1 * gutterX,
\r
296 gutterY: 1 * gutterY,
\r
303 innerWall[method](block.height);
\r
306 innerWall[method](block.width);
\r
309 innerWall[method](block.width, block.height);
\r
314 adjustBlock: function(block, setting) {
\r
315 var runtime = setting.runtime;
\r
316 var gutterX = runtime.gutterX;
\r
317 var gutterY = runtime.gutterY;
\r
318 var $item = $("#" + block.id);
\r
319 var cellH = runtime.cellH;
\r
320 var cellW = runtime.cellW;
\r
322 if (setting.cellH == 'auto') {
\r
323 $item.width(block.width * cellW - gutterX);
\r
324 $item[0].style.height = "";
\r
325 block.height = Math.round(($item.height() + gutterY) / cellH);
\r
328 adjustUnit: function(width, height, setting) {
\r
329 var gutterX = setting.gutterX;
\r
330 var gutterY = setting.gutterY;
\r
331 var runtime = setting.runtime;
\r
332 var cellW = setting.cellW;
\r
333 var cellH = setting.cellH;
\r
335 $.isFunction(cellW) && (cellW = cellW(width));
\r
337 !$.isNumeric(cellW) && (cellW = 1);
\r
339 $.isFunction(cellH) && (cellH = cellH(height));
\r
341 !$.isNumeric(cellH) && (cellH = 1);
\r
343 if ($.isNumeric(width)) {
\r
344 // adjust cell width via container;
\r
345 cellW < 1 && (cellW = cellW * width);
\r
347 // estimate total columns;
\r
348 var limitCol = Math.max(1, Math.floor(width / cellW));
\r
350 // adjust unit size for fit width;
\r
351 if (!$.isNumeric(gutterX)) {
\r
352 gutterX = (width - limitCol * cellW) / Math.max(1, (limitCol - 1));
\r
353 gutterX = Math.max(0, gutterX);
\r
356 limitCol = Math.floor((width + gutterX) / cellW);
\r
357 runtime.cellW = (width + gutterX) / Math.max(limitCol, 1);
\r
358 runtime.cellS = runtime.cellW / cellW;
\r
359 runtime.gutterX = gutterX;
\r
360 runtime.limitCol = limitCol;
\r
363 if ($.isNumeric(height)) {
\r
364 // adjust cell height via container;
\r
365 cellH < 1 && (cellH = cellH * height);
\r
367 // estimate total rows;
\r
368 var limitRow = Math.max(1, Math.floor(height / cellH));
\r
370 // adjust size unit for fit height;
\r
371 if (!$.isNumeric(gutterY)) {
\r
372 gutterY = (height - limitRow * cellH) / Math.max(1, (limitRow - 1));
\r
373 gutterY = Math.max(0, gutterY);
\r
376 limitRow = Math.floor((height + gutterY) / cellH);
\r
377 runtime.cellH = (height + gutterY) / Math.max(limitRow, 1);
\r
378 runtime.cellS = runtime.cellH / cellH;
\r
379 runtime.gutterY = gutterY;
\r
380 runtime.limitRow = limitRow;
\r
383 if (!$.isNumeric(width)) {
\r
384 // adjust cell width via cell height;
\r
385 cellW < 1 && (cellW = runtime.cellH);
\r
386 runtime.cellW = cellW != 1 ? cellW * runtime.cellS : 1;
\r
387 runtime.gutterX = gutterX;
\r
388 runtime.limitCol = 666666;
\r
391 if (!$.isNumeric(height)) {
\r
392 // adjust cell height via cell width;
\r
393 cellH < 1 && (cellH = runtime.cellW);
\r
394 runtime.cellH = cellH != 1 ? cellH * runtime.cellS : 1;
\r
395 runtime.gutterY = gutterY;
\r
396 runtime.limitRow = 666666;
\r
399 resetGrid: function(runtime) {
\r
400 runtime.blocks = {};
\r
401 runtime.length = 0;
\r
404 runtime.lastId = 1;
\r
405 runtime.matrix = {};
\r
406 runtime.totalCol = 0;
\r
407 runtime.totalRow = 0;
\r
409 setDraggable: function(item, option) {
\r
410 var isTouch = false;
\r
412 startX: 0, //start clientX;
\r
417 onDrop: function() {},
\r
418 onDrag: function() {},
\r
419 onStart: function() {}
\r
422 $(item).each(function() {
\r
423 var setting = $.extend({}, config, option);
\r
424 var handle = setting.handle || this;
\r
427 var $H = $(handle);
\r
429 var posStyle = $E.css("position");
\r
430 posStyle != "absolute" && $E.css("position", "relative");
\r
433 function mouseDown(evt) {
\r
434 evt.stopPropagation();
\r
435 evt = evt.originalEvent;
\r
439 evt = evt.changedTouches[0];
\r
442 if (evt.button != 2 && evt.which != 3) {
\r
443 setting.onStart.call(ele, evt);
\r
445 setting.startX = evt.clientX;
\r
446 setting.startY = evt.clientY;
\r
447 setting.top = parseInt($E.css("top")) || 0;
\r
448 setting.left = parseInt($E.css("left")) || 0;
\r
450 $D.bind("mouseup touchend", mouseUp);
\r
451 $D.bind("mousemove touchmove", mouseMove);
\r
458 function mouseMove(evt) {
\r
459 evt = evt.originalEvent;
\r
460 isTouch && (evt = evt.changedTouches[0]);
\r
463 top: setting.top - (setting.startY - evt.clientY),
\r
464 left: setting.left - (setting.startX - evt.clientX)
\r
467 setting.onDrag.call(ele, evt);
\r
470 function mouseUp(evt) {
\r
471 evt = evt.originalEvent;
\r
472 isTouch && (evt = evt.changedTouches[0]);
\r
474 setting.onDrop.call(ele, evt);
\r
476 $D.unbind("mouseup touchend", mouseUp);
\r
477 $D.unbind("mousemove touchmove", mouseMove);
\r
480 // ignore drag drop on text field;
\r
481 $E.find("iframe, form, input, textarea, .ignore-drag")
\r
483 $(this).on("touchstart mousedown", function(evt) {
\r
484 evt.stopPropagation();
\r
488 $D.unbind("mouseup touchend", mouseUp);
\r
489 $D.unbind("mousemove touchmove", mouseMove);
\r
490 $H.unbind("mousedown touchstart").bind("mousedown touchstart", mouseDown);
\r
494 setTransition: function(item, trans) {
\r
495 var style = item.style;
\r
496 var $item = $(item);
\r
498 // remove animation;
\r
499 if (!this.transition && $item.stop) {
\r
501 } else if (style.webkitTransition != null) {
\r
502 style.webkitTransition = trans;
\r
503 } else if (style.MozTransition != null) {
\r
504 style.MozTransition = trans;
\r
505 } else if (style.msTransition != null) {
\r
506 style.msTransition = trans;
\r
507 } else if (style.OTransition != null) {
\r
508 style.OTransition = trans;
\r
510 style.transition = trans;
\r
513 getFreeArea: function(t, l, runtime) {
\r
514 var maxY = Math.min(t + runtime.maxHoB, runtime.limitRow);
\r
515 var maxX = Math.min(l + runtime.maxWoB, runtime.limitCol);
\r
518 var matrix = runtime.matrix;
\r
520 // find limit zone by horizon;
\r
521 for (var y = t; y < minY; ++y) {
\r
522 for (var x = l; x < maxX; ++x) {
\r
523 if (matrix[y + '-' + x]) {
\r
524 (l < x && x < minX) && (minX = x);
\r
529 // find limit zone by vertical;
\r
530 for (var y = t; y < maxY; ++y) {
\r
531 for (var x = l; x < minX; ++x) {
\r
532 if (matrix[y + '-' + x]) {
\r
533 (t < y && y < minY) && (minY = y);
\r
546 setWallSize: function(runtime, container) {
\r
547 var totalRow = runtime.totalRow;
\r
548 var totalCol = runtime.totalCol;
\r
549 var gutterY = runtime.gutterY;
\r
550 var gutterX = runtime.gutterX;
\r
551 var cellH = runtime.cellH;
\r
552 var cellW = runtime.cellW;
\r
553 var totalWidth = Math.max(0, cellW * totalCol - gutterX);
\r
554 var totalHeight = Math.max(0, cellH * totalRow - gutterY);
\r
557 'data-total-col': totalCol,
\r
558 'data-total-row': totalRow,
\r
559 'data-wall-width': Math.ceil(totalWidth),
\r
560 'data-wall-height': Math.ceil(totalHeight)
\r
563 if (runtime.limitCol < runtime.limitRow) {
\r
564 // do not set height with nesting grid;
\r
565 !container.attr("data-height") && container.height(Math.ceil(totalHeight));
\r
573 // Giot just a person name;
\r
574 giot: function(items, setting) {
\r
575 var runtime = setting.runtime,
\r
576 row = runtime.limitRow,
\r
577 col = runtime.limitCol,
\r
580 maxX = runtime.totalCol,
\r
581 maxY = runtime.totalRow,
\r
583 holes = runtime.holes,
\r
585 matrix = runtime.matrix,
\r
586 bigLoop = Math.max(col, row),
\r
589 fitWidth = col < row ? 1 : 0,
\r
591 smallLoop = Math.min(col, row);
\r
593 // fill area with top, left, width, height;
\r
594 function fillMatrix(id, t, l, w, h) {
\r
595 for (var y = t; y < t + h;) {
\r
596 for (var x = l; x < l + w;) {
\r
597 matrix[y + '-' + x] = id;
\r
598 ++x > maxX && (maxX = x);
\r
600 ++y > maxY && (maxY = y);
\r
604 // set holes on the wall;
\r
605 for (var i in holes) {
\r
606 if (holes.hasOwnProperty(i)) {
\r
607 fillMatrix(holes[i]["id"] || true, holes[i]['top'], holes[i]['left'], holes[i]['width'], holes[i]['height']);
\r
612 for (var b = 0; b < bigLoop; ++b) {
\r
613 if (!items.length) break;
\r
614 fitWidth ? (y = b) : (x = b);
\r
617 for (var s = 0; s < smallLoop; ++s) {
\r
618 if (!items.length) break;
\r
620 fitWidth ? (x = s) : (y = s);
\r
621 if (runtime.matrix[y + '-' + x]) continue;
\r
622 freeArea = layoutManager.getFreeArea(y, x, runtime);
\r
624 // trying resize last block to fit free area;
\r
625 if (setting.fixSize == null) {
\r
626 // resize near block to fill gap;
\r
627 if (lastBlock && !fitWidth && runtime.minHoB > freeArea.height) {
\r
628 lastBlock.height += freeArea.height;
\r
629 lastBlock.resize = true;
\r
630 fillMatrix(lastBlock.id, lastBlock.y, lastBlock.x, lastBlock.width, lastBlock.height);
\r
631 layoutManager.setBlock(lastBlock, setting);
\r
633 } else if (lastBlock && fitWidth && runtime.minWoB > freeArea.width) {
\r
634 lastBlock.width += freeArea.width;
\r
635 lastBlock.resize = true;
\r
636 fillMatrix(lastBlock.id, lastBlock.y, lastBlock.x, lastBlock.width, lastBlock.height);
\r
637 layoutManager.setBlock(lastBlock, setting);
\r
642 // get the next block to keep order;
\r
643 if (setting.keepOrder) {
\r
644 block = items.shift();
\r
645 block.resize = true;
\r
647 // find a suitable block to fit gap;
\r
648 for (var i = 0; i < items.length; ++i) {
\r
649 if (items[i].height > freeArea.height) continue;
\r
650 if (items[i].width > freeArea.width) continue;
\r
651 block = items.splice(i, 1)[0];
\r
655 // trying resize the other block to fit gap;
\r
656 if (block == null && setting.fixSize == null) {
\r
657 // get other block fill to gap;
\r
658 for (var i = 0; i < items.length; ++i) {
\r
659 if (items[i]['fixSize'] != null) continue;
\r
660 block = items.splice(i, 1)[0];
\r
661 block.resize = true;
\r
669 if (block != null) {
\r
670 // resize block with free area;
\r
671 if (block.resize) {
\r
673 block.width = freeArea.width;
\r
674 if (setting.cellH == 'auto') {
\r
675 layoutManager.adjustBlock(block, setting);
\r
678 block.height = Math.min(block.height, freeArea.height);
\r
680 block.height = freeArea.height;
\r
682 block.width = Math.min(block.width, freeArea.width);
\r
690 width: block.width,
\r
691 height: block.height,
\r
692 resize: block.resize,
\r
693 fixSize: block.fixSize
\r
696 // keep success block for next round;
\r
697 lastBlock = wall[block.id];
\r
699 fillMatrix(lastBlock.id, lastBlock.y, lastBlock.x, lastBlock.width, lastBlock.height);
\r
700 layoutManager.setBlock(lastBlock, setting);
\r
702 // get expect area;
\r
709 misBlock.width = freeArea.width;
\r
710 misBlock.height = 0;
\r
714 while (matrix[lastY + '-' + lastX]) {
\r
715 matrix[lastY + '-' + x] = true;
\r
716 misBlock.height += 1;
\r
720 misBlock.height = freeArea.height;
\r
721 misBlock.width = 0;
\r
725 while (matrix[lastY + '-' + lastX]) {
\r
726 matrix[y + '-' + lastX] = true;
\r
727 misBlock.width += 1;
\r
731 setting.onGapFound(layoutManager.setBlock(misBlock, setting), setting);
\r
737 runtime.matrix = matrix;
\r
738 runtime.totalRow = maxY;
\r
739 runtime.totalCol = maxX;
\r
745 window.freewall = function(selector) {
\r
747 var container = $(selector);
\r
748 if (container.css('position') == 'static') {
\r
749 container.css('position', 'relative');
\r
751 var MAX = Number.MAX_VALUE;
\r
753 // increase the instance index;
\r
754 layoutManager.totalGrid += 1;
\r
756 var setting = $.extend({}, layoutManager.defaultConfig);
\r
758 blocks: {}, // store all items;
\r
759 events: {}, // store custome events;
\r
761 holes: {}, // forbidden zone;
\r
764 cellH: 0, // unit adjust;
\r
765 cellS: 1, // unit scale;
\r
767 filter: '', // filter selector;
\r
772 maxWoB: 0, // max width of block;
\r
775 minHoB: MAX, // min height of block;
\r
777 running: 0, // flag to check layout arranging;
\r
785 limitCol: 666666, // maximum column;
\r
788 currentMethod: null,
\r
789 currentArguments: []
\r
791 setting.runtime = runtime;
\r
792 runtime.totalGrid = layoutManager.totalGrid;
\r
794 // check browser support transition;
\r
795 var bodyStyle = document.body.style;
\r
796 if (!layoutManager.transition) {
\r
797 (bodyStyle.webkitTransition != null ||
\r
798 bodyStyle.MozTransition != null ||
\r
799 bodyStyle.msTransition != null ||
\r
800 bodyStyle.OTransition != null ||
\r
801 bodyStyle.transition != null) &&
\r
802 (layoutManager.transition = true);
\r
806 function setDraggable(item) {
\r
808 var gutterX = runtime.gutterX;
\r
809 var gutterY = runtime.gutterY;
\r
810 var cellH = runtime.cellH;
\r
811 var cellW = runtime.cellW;
\r
812 var $item = $(item);
\r
813 var handle = $item.find($item.attr("data-handle"));
\r
814 layoutManager.setDraggable(item, {
\r
816 onStart: function(event) {
\r
817 if (setting.animate && layoutManager.transition) {
\r
818 layoutManager.setTransition(this, "");
\r
820 $item.css('z-index', 9999).addClass('fw-float');
\r
822 setting.onBlockDrag.call(item, event);
\r
824 onDrag: function(event, tracker) {
\r
825 var position = $item.position();
\r
826 var top = Math.round(position.top / cellH);
\r
827 var left = Math.round(position.left / cellW);
\r
828 var width = Math.round($item.width() / cellW);
\r
829 var height = Math.round($item.height() / cellH);
\r
830 top = Math.min(Math.max(0, top), runtime.limitRow - height);
\r
831 left = Math.min(Math.max(0, left), runtime.limitCol - width);
\r
832 klass.setHoles({top: top, left: left, width: width, height: height});
\r
835 setting.onBlockMove.call(item, event);
\r
837 onDrop: function(event) {
\r
838 var position = $item.position();
\r
839 var top = Math.round(position.top / cellH);
\r
840 var left = Math.round(position.left / cellW);
\r
841 var width = Math.round($item.width() / cellW);
\r
842 var height = Math.round($item.height() / cellH);
\r
843 top = Math.min(Math.max(0, top), runtime.limitRow - height);
\r
844 left = Math.min(Math.max(0, left), runtime.limitCol - width);
\r
846 $item.removeClass('fw-float');
\r
853 //check old drag element;
\r
854 var x, y, key, oldDropId;
\r
855 for (y = 0; y < height; ++y) {
\r
856 for (x = 0; x < width; ++x) {
\r
857 key = (y + top) + "-" + (x + left);
\r
858 oldDropId = runtime.matrix[key];
\r
859 if (oldDropId && oldDropId != true) {
\r
860 $("#" + oldDropId).removeAttr("data-position");
\r
865 runtime.holes = {};
\r
868 "data-width": $item.width(),
\r
869 "data-height": $item.height(),
\r
870 "data-position": top + "-" + left
\r
875 setting.onBlockDrop.call(item, event);
\r
883 addCustomEvent: function(name, func) {
\r
884 var events = runtime.events;
\r
885 name = name.toLowerCase();
\r
886 !events[name] && (events[name] = []);
\r
887 func.eid = events[name].length;
\r
888 events[name].push(func);
\r
892 appendBlock: function(items) {
\r
893 var allBlock = $(items).appendTo(container);
\r
895 var activeBlock = [];
\r
897 if (runtime.currentMethod) {
\r
898 allBlock.each(function(index, item) {
\r
899 item.index = ++index;
\r
900 block = layoutManager.loadBlock(item, setting);
\r
901 block && activeBlock.push(block);
\r
904 engine[setting.engine](activeBlock, setting);
\r
906 layoutManager.setWallSize(runtime, container);
\r
908 runtime.length = allBlock.length;
\r
910 allBlock.each(function(index, item) {
\r
911 layoutManager.showBlock(item, setting);
\r
912 if (setting.draggable || item.getAttribute('data-draggable')) {
\r
913 setDraggable(item);
\r
919 add one or more blank area (hole) on layout;
\r
945 appendHoles: function(holes) {
\r
946 var newHoles = [].concat(holes), h = {}, i;
\r
947 for (i = 0; i < newHoles.length; ++i) {
\r
949 runtime.holes[h.top + "-" + h.left + "-" + h.width + "-" + h.height] = h;
\r
954 container: container,
\r
956 destroy: function() {
\r
957 var allBlock = container.find(setting.selector).removeAttr('id'),
\r
961 allBlock.each(function(index, item) {
\r
963 var width = 1 * $item.attr('data-width') || "";
\r
964 var height = 1 * $item.attr('data-height') || "";
\r
965 $item.width(width).height(height).css({
\r
971 fillHoles: function(holes) {
\r
972 if (arguments.length == 0) {
\r
973 runtime.holes = {};
\r
975 var newHoles = [].concat(holes), h = {}, i;
\r
976 for (i = 0; i < newHoles.length; ++i) {
\r
978 delete runtime.holes[h.top + "-" + h.left + "-" + h.width + "-" + h.height];
\r
984 filter: function(filter) {
\r
985 runtime.filter = filter;
\r
986 runtime.currentMethod && this.refresh();
\r
990 fireEvent: function(name, object, setting) {
\r
991 var events = runtime.events;
\r
992 name = name.toLowerCase();
\r
993 if (events[name] && events[name].length) {
\r
994 for (var i = 0; i < events[name].length; ++i) {
\r
995 events[name][i].call(this, object, setting);
\r
1001 fitHeight: function(height) {
\r
1002 var allBlock = container.find(setting.selector).removeAttr('id'),
\r
1006 height = height ? height : container.height() || $W.height();
\r
1008 runtime.currentMethod = arguments.callee;
\r
1009 runtime.currentArguments = arguments;
\r
1011 layoutManager.resetGrid(runtime);
\r
1012 layoutManager.adjustUnit('auto', height, setting);
\r
1014 if (runtime.filter) {
\r
1015 allBlock.data('active', 0);
\r
1016 allBlock.filter(runtime.filter).data('active', 1);
\r
1018 allBlock.data('active', 1);
\r
1021 allBlock.each(function(index, item) {
\r
1022 var $item = $(item);
\r
1023 item.index = ++index;
\r
1024 block = layoutManager.loadBlock(item, setting);
\r
1025 block && $item.data("active") && activeBlock.push(block);
\r
1028 klass.fireEvent('onGridReady', container, setting);
\r
1030 engine[setting.engine](activeBlock, setting);
\r
1032 layoutManager.setWallSize(runtime, container);
\r
1034 klass.fireEvent('onGridArrange', container, setting);
\r
1036 runtime.length = allBlock.length;
\r
1038 allBlock.each(function(index, item) {
\r
1039 layoutManager.showBlock(item, setting);
\r
1040 if (setting.draggable || item.getAttribute('data-draggable')) {
\r
1041 setDraggable(item);
\r
1046 fitWidth: function(width) {
\r
1047 var allBlock = container.find(setting.selector).removeAttr('id'),
\r
1051 width = width ? width : container.width() || $W.width();
\r
1053 runtime.currentMethod = arguments.callee;
\r
1054 runtime.currentArguments = arguments;
\r
1056 layoutManager.resetGrid(runtime);
\r
1057 layoutManager.adjustUnit(width, 'auto', setting);
\r
1059 if (runtime.filter) {
\r
1060 allBlock.data('active', 0);
\r
1061 allBlock.filter(runtime.filter).data('active', 1);
\r
1063 allBlock.data('active', 1);
\r
1066 allBlock.each(function(index, item) {
\r
1067 var $item = $(item);
\r
1068 item.index = ++index;
\r
1069 block = layoutManager.loadBlock(item, setting);
\r
1070 block && $item.data("active") && activeBlock.push(block);
\r
1073 klass.fireEvent('onGridReady', container, setting);
\r
1075 engine[setting.engine](activeBlock, setting);
\r
1077 layoutManager.setWallSize(runtime, container);
\r
1079 klass.fireEvent('onGridArrange', container, setting);
\r
1081 runtime.length = allBlock.length;
\r
1083 allBlock.each(function(index, item) {
\r
1084 layoutManager.showBlock(item, setting);
\r
1085 if (setting.draggable || item.getAttribute('data-draggable')) {
\r
1086 setDraggable(item);
\r
1091 fitZone: function(width, height) {
\r
1092 var allBlock = container.find(setting.selector).removeAttr('id'),
\r
1096 height = height ? height : container.height() || $W.height();
\r
1097 width = width ? width : container.width() || $W.width();
\r
1099 runtime.currentMethod = arguments.callee;
\r
1100 runtime.currentArguments = arguments;
\r
1102 layoutManager.resetGrid(runtime);
\r
1103 layoutManager.adjustUnit(width, height, setting);
\r
1105 if (runtime.filter) {
\r
1106 allBlock.data('active', 0);
\r
1107 allBlock.filter(runtime.filter).data('active', 1);
\r
1109 allBlock.data('active', 1);
\r
1112 allBlock.each(function(index, item) {
\r
1113 var $item = $(item);
\r
1114 item.index = ++index;
\r
1115 block = layoutManager.loadBlock(item, setting);
\r
1116 block && $item.data("active") && activeBlock.push(block);
\r
1119 klass.fireEvent('onGridReady', container, setting);
\r
1121 engine[setting.engine](activeBlock, setting);
\r
1123 layoutManager.setWallSize(runtime, container);
\r
1125 klass.fireEvent('onGridArrange', container, setting);
\r
1127 runtime.length = allBlock.length;
\r
1129 allBlock.each(function(index, item) {
\r
1130 layoutManager.showBlock(item, setting);
\r
1131 if (setting.draggable || item.getAttribute('data-draggable')) {
\r
1132 setDraggable(item);
\r
1138 set block with special position, the top and left are multiple of unit width/height;
\r
1147 fixPos: function(option) {
\r
1148 $(option.block).attr({'data-position': option.top + "-" + option.left});
\r
1153 set block with special size, the width and height are multiple of unit width/height;
\r
1162 fixSize: function(option) {
\r
1163 option.height != null && $(option.block).attr({'data-height': option.height});
\r
1164 option.width != null && $(option.block).attr({'data-width': option.width});
\r
1168 prepend: function(items) {
\r
1169 container.prepend(items);
\r
1170 runtime.currentMethod && this.refresh();
\r
1174 refresh: function() {
\r
1175 var params = arguments.length ? arguments : runtime.currentArguments;
\r
1176 runtime.currentMethod == null && (runtime.currentMethod = this.fitWidth);
\r
1177 runtime.currentMethod.apply(this, Array.prototype.slice.call(params, 0));
\r
1182 custom layout setting;
\r
1186 selector: '.brick',
\r
1191 onResize: function() {
\r
1196 reset: function(option) {
\r
1197 $.extend(setting, option);
\r
1202 create one or more blank area (hole) on layout;
\r
1213 setHoles: function(holes) {
\r
1214 var newHoles = [].concat(holes), h = {}, i;
\r
1215 runtime.holes = {};
\r
1216 for (i = 0; i < newHoles.length; ++i) {
\r
1218 runtime.holes[h.top + "-" + h.left + "-" + h.width + "-" + h.height] = h;
\r
1223 unFilter: function() {
\r
1224 delete runtime.filter;
\r
1230 container.attr('data-min-width', Math.floor($W.width() / 80) * 80);
\r
1231 // execute plugins;
\r
1232 for (var i in layoutManager.plugin) {
\r
1233 if (layoutManager.plugin.hasOwnProperty(i)) {
\r
1234 layoutManager.plugin[i].call(klass, setting, container);
\r
1238 // setup resize event;
\r
1239 $W.resize(function() {
\r
1240 if (runtime.running) return;
\r
1241 runtime.running = 1;
\r
1242 setTimeout(function() {
\r
1243 runtime.running = 0;
\r
1244 setting.onResize.call(klass, container);
\r
1246 container.attr('data-min-width', Math.floor($W.width() / 80) * 80);
\r
1251 add default setting;
\r
1254 freewall.addConfig({
\r
1258 freewall.addConfig = function(newConfig) {
\r
1259 // add default setting;
\r
1260 $.extend(layoutManager.defaultConfig, newConfig);
\r
1265 support create new arrange algorithm;
\r
1268 freewall.createEngine({
\r
1269 slice: function(items, setting) {
\r
1274 freewall.createEngine = function(engineData) {
\r
1275 // create new engine;
\r
1276 $.extend(engine, engineData);
\r
1280 support create new plugin;
\r
1283 freewall.createPlugin({
\r
1284 centering: function(setting, container) {
\r
1285 console.log(this);
\r
1286 console.log(setting);
\r
1290 freewall.createPlugin = function(pluginData) {
\r
1291 // register new plugin;
\r
1292 $.extend(layoutManager.plugin, pluginData);
\r
1296 support access helper function;
\r
1299 freewall.getMethod('setBlock')(block, setting);
\r
1301 freewall.getMethod = function(method) {
\r
1302 // get helper method;
\r
1303 return layoutManager[method];
\r
1306 })(window.Zepto || window.jQuery);
\r