1// created by Minh Nguyen; 2// version 1.05; 3 4(function($) { 5 6 // for zeptojs; 7 $.isNumeric == null && ($.isNumeric = function(src) { 8 return src != null && src.constructor === Number; 9 }); 10 11 $.isFunction == null && ($.isFunction = function(src) { 12 return src != null && src instanceof Function; 13 }); 14 15 var $W = $(window); 16 var $D = $(document); 17 18 var layoutManager = { 19 // default setting; 20 defaultConfig: { 21 animate: false, 22 cellW: 100, // function(container) {return 100;} 23 cellH: 100, // function(container) {return 100;} 24 delay: 0, // slowdown active block; 25 engine: 'giot', // 'giot' is a person name; 26 fixSize: null, // resize + adjust = fill gap; 27 //fixSize: 0, resize but keep ratio = no fill gap; 28 //fixSize: 1, no resize + no adjust = no fill gap; 29 gutterX: 30, // width spacing between blocks; 30 gutterY: 30, // height spacing between blocks; 31 keepOrder: false, 32 selector: '> div', 33 draggable: false, 34 cacheSize: true, // caches the original size of block; 35 rightToLeft: false, 36 bottomToTop: false, 37 onGapFound: function() {}, 38 onComplete: function() {}, 39 onResize: function() {}, 40 onBlockDrag: function() {}, 41 onBlockMove: function() {}, 42 onBlockDrop: function() {}, 43 onBlockReady: function() {}, 44 onBlockFinish: function() {}, 45 onBlockActive: function() {}, 46 onBlockResize: function() {} 47 }, 48 plugin: {}, 49 totalGrid: 1, 50 transition: false, 51 loadBlock: function(item, setting) { 52 var runtime = setting.runtime; 53 var gutterX = runtime.gutterX; 54 var gutterY = runtime.gutterY; 55 var cellH = runtime.cellH; 56 var cellW = runtime.cellW; 57 var block = null; 58 var $item = $(item); 59 var active = $item.data("active"); 60 var fixPos = $item.attr('data-position'); 61 var fixSize = parseInt($item.attr('data-fixSize')); 62 var blockId = runtime.lastId++ + '-' + runtime.totalGrid; 63 64 //ignore dragging block; 65 if ($item.hasClass('fw-float')) return null; 66 $item.attr({id: blockId, 'data-delay': item.index}); 67 68 //remove animation for speed render; 69 if (setting.animate && this.transition) { 70 this.setTransition(item, ""); 71 } 72 73 isNaN(fixSize) && (fixSize = null); 74 (fixSize == null) && (fixSize = setting.fixSize); 75 var makeRound = (!fixSize) ? "round" : "ceil"; 76 // store original size; 77 78 $item.attr('data-height') == null && $item.attr('data-height', $item.height()); 79 $item.attr('data-width') == null && $item.attr('data-width', $item.width()); 80 var height = 1 * $item.attr('data-height'); 81 var width = 1 * $item.attr('data-width'); 82 83 if (!setting.cacheSize) { 84 item.style.width = ""; 85 width = $item.width(); 86 87 item.style.height = ""; 88 height = $item.height(); 89 } 90 91 var col = !width ? 0 : Math[makeRound]((width + gutterX) / cellW); 92 var row = !height ? 0 : Math[makeRound]((height + gutterY) / cellH); 93 94 // estimate size; 95 if (!fixSize && setting.cellH == 'auto') { 96 $item.width(cellW * col - gutterX); 97 item.style.height = ""; 98 height = $item.height(); 99 row = !height ? 0 : Math.round((height + gutterY) / cellH); 100 } 101 102 if (!fixSize && setting.cellW == 'auto') { 103 $item.height(cellH * row - gutterY); 104 item.style.width = ""; 105 width = $item.width(); 106 col = !width ? 0 : Math.round((width + gutterX) / cellW); 107 } 108 109 // for none resize block; 110 if ((fixSize != null) && (col > runtime.limitCol || row > runtime.limitRow)) { 111 block = null; 112 } else { 113 // get smallest width and smallest height of block; 114 // using for image runtime; 115 row && row < runtime.minHoB && (runtime.minHoB = row); 116 col && col < runtime.minWoB && (runtime.minWoB = col); 117 118 // get biggest width and biggest height of block; 119 row > runtime.maxHoB && (runtime.maxHoB = row); 120 col > runtime.maxWoB && (runtime.maxWoB = col); 121 122 width == 0 && (col = 0); 123 height == 0 && (row = 0); 124 125 block = { 126 resize: false, 127 id: blockId, 128 width: col, 129 height: row, 130 fixSize: fixSize 131 }; 132 133 // for fix position; 134 if (fixPos) { 135 fixPos = fixPos.split("-"); 136 block.y = 1 * fixPos[0]; 137 block.x = 1 * fixPos[1]; 138 block.width = fixSize != null ? col : Math.min(col, runtime.limitCol - block.x); 139 block.height = fixSize != null ? row : Math.min(row, runtime.limitRow - block.y); 140 var holeId = block.y + "-" + block.x + "-" + block.width + "-" + block.height; 141 if (active) { 142 runtime.holes[holeId] = { 143 id: block.id, 144 top: block.y, 145 left: block.x, 146 width: block.width, 147 height: block.height 148 }; 149 this.setBlock(block, setting); 150 } else { 151 delete runtime.holes[holeId]; 152 } 153 154 } 155 } 156 157 // for css animation; 158 if ($item.attr("data-state") == null) { 159 $item.attr("data-state", "init"); 160 } else { 161 $item.attr("data-state", "move"); 162 } 163 164 setting.onBlockReady.call(item, block, setting); 165 166 return (fixPos && active) ? null : block; 167 }, 168 setBlock: function(block, setting) { 169 var runtime = setting.runtime; 170 var gutterX = runtime.gutterX; 171 var gutterY = runtime.gutterY; 172 var height = block.height; 173 var width = block.width; 174 var cellH = runtime.cellH; 175 var cellW = runtime.cellW; 176 var x = block.x; 177 var y = block.y; 178 179 if (setting.rightToLeft) { 180 x = runtime.limitCol - x - width; 181 } 182 if (setting.bottomToTop) { 183 y = runtime.limitRow - y - height; 184 } 185 186 var realBlock = { 187 fixSize: block.fixSize, 188 resize: block.resize, 189 top: y * cellH, 190 left: x * cellW, 191 width: cellW * width - gutterX, 192 height: cellH * height - gutterY 193 }; 194 195 realBlock.top = 1 * realBlock.top.toFixed(2); 196 realBlock.left = 1 * realBlock.left.toFixed(2); 197 realBlock.width = 1 * realBlock.width.toFixed(2); 198 realBlock.height = 1 * realBlock.height.toFixed(2); 199 200 //runtime.length += 1; 201 block.id && (runtime.blocks[block.id] = realBlock); 202 203 // for append feature; 204 return realBlock; 205 }, 206 showBlock: function(item, setting) { 207 var runtime = setting.runtime; 208 var method = setting.animate && !this.transition ? 'animate' : 'css'; 209 var block = runtime.blocks[item.id]; 210 var $item = $(item); 211 var self = this; 212 var start = $item.attr("data-state") != "move"; 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"; 214 215 item.delay && clearTimeout(item.delay); 216 //ignore dragging block; 217 if ($item.hasClass('fw-float')) return; 218 219 // kill the old transition; 220 self.setTransition(item, ""); 221 item.style.position = "absolute"; 222 setting.onBlockActive.call(item, block, setting); 223 224 function action() { 225 // start to arrange; 226 start && $item.attr("data-state", "start"); 227 // add animation by using css3 transition; 228 if (setting.animate && self.transition) { 229 self.setTransition(item, trans); 230 } 231 232 // for hidden block; 233 if (!block) { 234 //var position = $item.position(); <= make speed so slow; 235 var height = parseInt(item.style.height) || 0; 236 var width = parseInt(item.style.width) || 0; 237 var left = parseInt(item.style.left) || 0; 238 var top = parseInt(item.style.top) || 0; 239 $item[method]({ 240 left: left + width / 2, 241 top: top + height / 2, 242 width: 0, 243 height: 0, 244 opacity: 0 245 }); 246 } else { 247 if (block.fixSize) { 248 block.height = 1 * $item.attr("data-height"); 249 block.width = 1 * $item.attr("data-width"); 250 } 251 252 $item["css"]({ 253 opacity: 1, 254 width: block.width, 255 height: block.height 256 }); 257 258 // for animating by javascript; 259 $item[method]({ 260 top: block.top, 261 left: block.left 262 }); 263 264 if ($item.attr('data-nested') != null) { 265 self.nestedGrid(item, setting); 266 } 267 } 268 269 runtime.length -= 1; 270 271 setting.onBlockFinish.call(item, block, setting); 272 273 runtime.length == 0 && setting.onComplete.call(item, block, setting); 274 } 275 276 block && block.resize && setting.onBlockResize.call(item, block, setting); 277 278 setting.delay > 0 ? (item.delay = setTimeout(action, setting.delay * $item.attr("data-delay"))) : action(); 279 }, 280 nestedGrid: function(item, setting) { 281 var innerWall, $item = $(item), runtime = setting.runtime; 282 var gutterX = $item.attr("data-gutterX") || setting.gutterX; 283 var gutterY = $item.attr("data-gutterY") || setting.gutterY; 284 var method = $item.attr("data-method") || "fitZone"; 285 var nested = $item.attr('data-nested') || "> div"; 286 var cellH = $item.attr("data-cellH") || setting.cellH; 287 var cellW = $item.attr("data-cellW") || setting.cellW; 288 var block = runtime.blocks[item.id]; 289 290 if (block) { 291 innerWall = new Freewall($item); 292 innerWall.reset({ 293 cellH: cellH, 294 cellW: cellW, 295 gutterX: 1 * gutterX, 296 gutterY: 1 * gutterY, 297 selector: nested, 298 cacheSize: false 299 }); 300 301 switch (method) { 302 case "fitHeight": 303 innerWall[method](block.height); 304 break; 305 case "fitWidth": 306 innerWall[method](block.width); 307 break; 308 case "fitZone": 309 innerWall[method](block.width, block.height); 310 break; 311 } 312 } 313 }, 314 adjustBlock: function(block, setting) { 315 var runtime = setting.runtime; 316 var gutterX = runtime.gutterX; 317 var gutterY = runtime.gutterY; 318 var $item = $("#" + block.id); 319 var cellH = runtime.cellH; 320 var cellW = runtime.cellW; 321 322 if (setting.cellH == 'auto') { 323 $item.width(block.width * cellW - gutterX); 324 $item[0].style.height = ""; 325 block.height = Math.round(($item.height() + gutterY) / cellH); 326 } 327 }, 328 adjustUnit: function(width, height, setting) { 329 var gutterX = setting.gutterX; 330 var gutterY = setting.gutterY; 331 var runtime = setting.runtime; 332 var cellW = setting.cellW; 333 var cellH = setting.cellH; 334 335 $.isFunction(cellW) && (cellW = cellW(width)); 336 cellW = 1 * cellW; 337 !$.isNumeric(cellW) && (cellW = 1); 338 339 $.isFunction(cellH) && (cellH = cellH(height)); 340 cellH = 1 * cellH; 341 !$.isNumeric(cellH) && (cellH = 1); 342 343 if ($.isNumeric(width)) { 344 // adjust cell width via container; 345 cellW < 1 && (cellW = cellW * width); 346 347 // estimate total columns; 348 var limitCol = Math.max(1, Math.floor(width / cellW)); 349 350 // adjust unit size for fit width; 351 if (!$.isNumeric(gutterX)) { 352 gutterX = (width - limitCol * cellW) / Math.max(1, (limitCol - 1)); 353 gutterX = Math.max(0, gutterX); 354 } 355 356 limitCol = Math.floor((width + gutterX) / cellW); 357 runtime.cellW = (width + gutterX) / Math.max(limitCol, 1); 358 runtime.cellS = runtime.cellW / cellW; 359 runtime.gutterX = gutterX; 360 runtime.limitCol = limitCol; 361 } 362 363 if ($.isNumeric(height)) { 364 // adjust cell height via container; 365 cellH < 1 && (cellH = cellH * height); 366 367 // estimate total rows; 368 var limitRow = Math.max(1, Math.floor(height / cellH)); 369 370 // adjust size unit for fit height; 371 if (!$.isNumeric(gutterY)) { 372 gutterY = (height - limitRow * cellH) / Math.max(1, (limitRow - 1)); 373 gutterY = Math.max(0, gutterY); 374 } 375 376 limitRow = Math.floor((height + gutterY) / cellH); 377 runtime.cellH = (height + gutterY) / Math.max(limitRow, 1); 378 runtime.cellS = runtime.cellH / cellH; 379 runtime.gutterY = gutterY; 380 runtime.limitRow = limitRow; 381 } 382 383 if (!$.isNumeric(width)) { 384 // adjust cell width via cell height; 385 cellW < 1 && (cellW = runtime.cellH); 386 runtime.cellW = cellW != 1 ? cellW * runtime.cellS : 1; 387 runtime.gutterX = gutterX; 388 runtime.limitCol = 666666; 389 } 390 391 if (!$.isNumeric(height)) { 392 // adjust cell height via cell width; 393 cellH < 1 && (cellH = runtime.cellW); 394 runtime.cellH = cellH != 1 ? cellH * runtime.cellS : 1; 395 runtime.gutterY = gutterY; 396 runtime.limitRow = 666666; 397 } 398 399 runtime.keepOrder = setting.keepOrder; 400 }, 401 resetGrid: function(runtime) { 402 runtime.blocks = {}; 403 runtime.length = 0; 404 runtime.cellH = 0; 405 runtime.cellW = 0; 406 runtime.lastId = 1; 407 runtime.matrix = {}; 408 runtime.totalCol = 0; 409 runtime.totalRow = 0; 410 }, 411 setDraggable: function(item, option) { 412 var isTouch = false;
413 var config = { 414 startX: 0, //start clientX; 415 startY: 0, 416 top: 0, 417 left: 0, 418 handle: null, 419 onDrop: function() {}, 420 onDrag: function() {}, 421 onStart: function() {} 422 }; 423 424 $(item).each(function() { 425 var setting = $.extend({}, config, option); 426 var handle = setting.handle || this; 427 var ele = this; 428 var $E = $(ele); 429 var $H = $(handle); 430 431 var posStyle = $E.css("position"); 432 posStyle != "absolute" && $E.css("position", "relative"); 433 434 435 function mouseDown(evt) { 436 evt.stopPropagation(); 437 evt = evt.originalEvent; 438 439 if (evt.touches) { 440 isTouch = true; 441 evt = evt.changedTouches[0]; 442 } 443 444 if (evt.button != 2 && evt.which != 3) { 445 setting.onStart.call(ele, evt); 446 447 setting.startX = evt.clientX; 448 setting.startY = evt.clientY; 449 setting.top = parseInt($E.css("top")) || 0; 450 setting.left = parseInt($E.css("left")) || 0; 451 452 $D.bind("mouseup touchend", mouseUp); 453 $D.bind("mousemove touchmove", mouseMove); 454 } 455 456 return false; 457 }; 458 459 460 function mouseMove(evt) { 461 evt = evt.originalEvent; 462 isTouch && (evt = evt.changedTouches[0]); 463 464 $E.css({ 465 top: setting.top - (setting.startY - evt.clientY), 466 left: setting.left - (setting.startX - evt.clientX) 467 }); 468 469 setting.onDrag.call(ele, evt); 470 }; 471 472 function mouseUp(evt) { 473 evt = evt.originalEvent; 474 isTouch && (evt = evt.changedTouches[0]); 475 476 setting.onDrop.call(ele, evt); 477 478 $D.unbind("mouseup touchend", mouseUp); 479 $D.unbind("mousemove touchmove", mouseMove); 480 }; 481 482 // ignore drag drop on text field; 483 $E.find("iframe, form, input, textarea, .ignore-drag") 484 .each(function() { 485 $(this).on("touchstart mousedown", function(evt) { 486 evt.stopPropagation(); 487 }); 488 }); 489 490 $D.unbind("mouseup touchend", mouseUp); 491 $D.unbind("mousemove touchmove", mouseMove); 492 $H.unbind("mousedown touchstart").bind("mousedown touchstart", mouseDown); 493 494 }); 495 }, 496 setTransition: function(item, trans) { 497 var style = item.style; 498 var $item = $(item); 499 500 // remove animation; 501 if (!this.transition && $item.stop) { 502 $item.stop(); 503 } else if (style.webkitTransition != null) { 504 style.webkitTransition = trans; 505 } else if (style.MozTransition != null) { 506 style.MozTransition = trans; 507 } else if (style.msTransition != null) { 508 style.msTransition = trans; 509 } else if (style.OTransition != null) { 510 style.OTransition = trans; 511 } else { 512 style.transition = trans; 513 } 514 }, 515 getFreeArea: function(t, l, runtime) { 516 var maxY = Math.min(t + runtime.maxHoB, runtime.limitRow); 517 var maxX = Math.min(l + runtime.maxWoB, runtime.limitCol); 518 var minX = maxX; 519 var minY = maxY; 520 var matrix = runtime.matrix; 521 522 // find limit zone by horizon; 523 for (var y = t; y < minY; ++y) { 524 for (var x = l; x < maxX; ++x) { 525 if (matrix[y + '-' + x]) { 526 (l < x && x < minX) && (minX = x); 527 } 528 } 529 } 530 531 // find limit zone by vertical; 532 for (var y = t; y < maxY; ++y) { 533 for (var x = l; x < minX; ++x) { 534 if (matrix[y + '-' + x]) { 535 (t < y && y < minY) && (minY = y); 536 } 537 } 538 } 539 540 return { 541 top: t, 542 left: l, 543 width: minX - l, 544 height: minY - t 545 }; 546 547 }, 548 setWallSize: function(runtime, container) { 549 var totalRow = runtime.totalRow; 550 var totalCol = runtime.totalCol; 551 var gutterY = runtime.gutterY; 552 var gutterX = runtime.gutterX; 553 var cellH = runtime.cellH; 554 var cellW = runtime.cellW; 555 var totalWidth = Math.max(0, cellW * totalCol - gutterX); 556 var totalHeight = Math.max(0, cellH * totalRow - gutterY); 557 558 container.attr({ 559 'data-total-col': totalCol, 560 'data-total-row': totalRow,
561 'data-wall-width': Math.ceil(totalWidth), 562 'data-wall-height': Math.ceil(totalHeight) 563 }); 564 565 if (runtime.limitCol < runtime.limitRow) { 566 // do not set height with nesting grid; 567 !container.attr("data-height") && container.height(Math.ceil(totalHeight)); 568 } 569 } 570 }; 571 572 573 574 var engine = { 575 // Giot just a person name; 576 giot: function(items, setting) { 577 var runtime = setting.runtime, 578 row = runtime.limitRow, 579 col = runtime.limitCol, 580 x = 0, 581 y = 0, 582 maxX = runtime.totalCol, 583 maxY = runtime.totalRow, 584 wall = {}, 585 holes = runtime.holes, 586 block = null, 587 matrix = runtime.matrix, 588 bigLoop = Math.max(col, row), 589 freeArea = null, 590 misBlock = null, 591 fitWidth = col < row ? 1 : 0, 592 lastBlock = null, 593 smallLoop = Math.min(col, row); 594 595 // fill area with top, left, width, height; 596 function fillMatrix(id, t, l, w, h) { 597 for (var y = t; y < t + h;) { 598 for (var x = l; x < l + w;) { 599 matrix[y + '-' + x] = id; 600 ++x > maxX && (maxX = x); 601 } 602 ++y > maxY && (maxY = y); 603 } 604 } 605 606 // set holes on the wall; 607 for (var i in holes) { 608 if (holes.hasOwnProperty(i)) { 609 fillMatrix(holes[i]["id"] || true, holes[i]['top'], holes[i]['left'], holes[i]['width'], holes[i]['height']); 610 } 611 } 612 613 614 for (var b = 0; b < bigLoop; ++b) { 615 if (!items.length) break; 616 fitWidth ? (y = b) : (x = b); 617 lastBlock = null; 618 619 for (var s = 0; s < smallLoop; ++s) { 620 if (!items.length) break; 621 block = null; 622 fitWidth ? (x = s) : (y = s); 623 if (runtime.matrix[y + '-' + x]) continue; 624 freeArea = layoutManager.getFreeArea(y, x, runtime); 625 626 // trying resize last block to fit free area; 627 if (setting.fixSize == null) { 628 // resize near block to fill gap; 629 if (lastBlock && !fitWidth && runtime.minHoB > freeArea.height) { 630 lastBlock.height += freeArea.height; 631 lastBlock.resize = true; 632 fillMatrix(lastBlock.id, lastBlock.y, lastBlock.x, lastBlock.width, lastBlock.height); 633 layoutManager.setBlock(lastBlock, setting); 634 continue; 635 } else if (lastBlock && fitWidth && runtime.minWoB > freeArea.width) { 636 lastBlock.width += freeArea.width; 637 lastBlock.resize = true; 638 fillMatrix(lastBlock.id, lastBlock.y, lastBlock.x, lastBlock.width, lastBlock.height); 639 layoutManager.setBlock(lastBlock, setting); 640 continue; 641 } 642 } 643 644 // get the next block to keep order; 645 if (runtime.keepOrder) { 646 block = items.shift(); 647 block.resize = true; 648 } else { 649 // find a suitable block to fit gap; 650 for (var i = 0; i < items.length; ++i) { 651 if (items[i].height > freeArea.height) continue; 652 if (items[i].width > freeArea.width) continue; 653 block = items.splice(i, 1)[0]; 654 break; 655 } 656 657 // trying resize the other block to fit gap; 658 if (block == null && setting.fixSize == null) { 659 // get other block fill to gap; 660 for (var i = 0; i < items.length; ++i) { 661 if (items[i]['fixSize'] != null) continue; 662 block = items.splice(i, 1)[0]; 663 block.resize = true; 664 break; 665 } 666 667 } 668 } 669 670 671 if (block != null) { 672 // resize block with free area; 673 if (block.resize) { 674 if (fitWidth) { 675 block.width = freeArea.width; 676 if (setting.cellH == 'auto') { 677 layoutManager.adjustBlock(block, setting); 678 } 679 // for fitZone; 680 block.height = Math.min(block.height, freeArea.height); 681 } else { 682 block.height = freeArea.height; 683 // for fitZone; 684 block.width = Math.min(block.width, freeArea.width); 685 } 686 } 687 688 wall[block.id] = { 689 id: block.id, 690 x: x, 691 y: y, 692 width: block.width, 693 height: block.height, 694 resize: block.resize, 695 fixSize: block.fixSize 696 }; 697 698 // keep success block for next round; 699 lastBlock = wall[block.id]; 700 701 fillMatrix(lastBlock.id, lastBlock.y, lastBlock.x, lastBlock.width, lastBlock.height); 702 layoutManager.setBlock(lastBlock, setting); 703 } else { 704 // get expect area; 705 var misBlock = { 706 x: x, 707 y: y, 708 fixSize: 0 709 }; 710 if (fitWidth) { 711 misBlock.width = freeArea.width; 712 misBlock.height = 0; 713 var lastX = x - 1; 714 var lastY = y; 715 716 while (matrix[lastY + '-' + lastX]) { 717 matrix[lastY + '-' + x] = true; 718 misBlock.height += 1; 719 lastY += 1; 720 } 721 } else { 722 misBlock.height = freeArea.height; 723 misBlock.width = 0; 724 var lastY = y - 1; 725 var lastX = x; 726 727 while (matrix[lastY + '-' + lastX]) { 728 matrix[y + '-' + lastX] = true; 729 misBlock.width += 1; 730 lastX += 1; 731 } 732 } 733 setting.onGapFound(layoutManager.setBlock(misBlock, setting), setting); 734 } 735 } 736 737 } 738 739 runtime.matrix = matrix; 740 runtime.totalRow = maxY; 741 runtime.totalCol = maxX; 742 } 743 }; 744 745 746 747 function Freewall(selector) { 748 749 var container = $(selector); 750 if (container.css('position') == 'static') { 751 container.css('position', 'relative'); 752 } 753 var MAX = Number.MAX_VALUE; 754 var klass = this; 755 // increase the instance index; 756 layoutManager.totalGrid += 1; 757 758 var setting = $.extend({}, layoutManager.defaultConfig); 759 var runtime = { 760 arguments: null, 761 blocks: {}, // store all items; 762 events: {}, // store custome events; 763 matrix: {}, 764 holes: {}, // forbidden zone; 765 766 cellW: 0, 767 cellH: 0, // unit adjust; 768 cellS: 1, // unit scale; 769
770 filter: '', // filter selector; 771 lastId: 0, 772 length: 0, 773 774 maxWoB: 0, // max width of block; 775 maxHoB: 0, 776 minWoB: MAX, 777 minHoB: MAX, // min height of block; 778 779 running: 0, // flag to check layout arranging; 780 781 gutterX: 15, 782 gutterY: 15, 783 784 totalCol: 0, 785 totalRow: 0, 786 787 limitCol: 666666, // maximum column; 788 limitRow: 666666, 789 790 sortFunc: null, 791 keepOrder: false 792 }; 793 setting.runtime = runtime; 794 runtime.totalGrid = layoutManager.totalGrid; 795 796 // check browser support transition; 797 var bodyStyle = document.body.style; 798 if (!layoutManager.transition) { 799 (bodyStyle.webkitTransition != null || 800 bodyStyle.MozTransition != null || 801 bodyStyle.msTransition != null || 802 bodyStyle.OTransition != null || 803 bodyStyle.transition != null) && 804 (layoutManager.transition = true); 805 } 806 807 808 function setDraggable(item) { 809 810 var gutterX = runtime.gutterX; 811 var gutterY = runtime.gutterY; 812 var cellH = runtime.cellH; 813 var cellW = runtime.cellW; 814 var $item = $(item); 815 var handle = $item.find($item.attr("data-handle")); 816 layoutManager.setDraggable(item, { 817 handle: handle[0], 818 onStart: function(event) { 819 if (setting.animate && layoutManager.transition) { 820 layoutManager.setTransition(this, ""); 821 } 822 $item.css('z-index', 9999).addClass('fw-float'); 823 824 setting.onBlockDrag.call(item, event); 825 }, 826 onDrag: function(event, tracker) { 827 var position = $item.position(); 828 var top = Math.round(position.top / cellH); 829 var left = Math.round(position.left / cellW); 830 var width = Math.round($item.width() / cellW); 831 var height = Math.round($item.height() / cellH); 832 top = Math.min(Math.max(0, top), runtime.limitRow - height); 833 left = Math.min(Math.max(0, left), runtime.limitCol - width); 834 klass.setHoles({top: top, left: left, width: width, height: height}); 835 klass.refresh(); 836 837 setting.onBlockMove.call(item, event); 838 }, 839 onDrop: function(event) { 840 var position = $item.position(); 841 var top = Math.round(position.top / cellH); 842 var left = Math.round(position.left / cellW); 843 var width = Math.round($item.width() / cellW); 844 var height = Math.round($item.height() / cellH); 845 top = Math.min(Math.max(0, top), runtime.limitRow - height); 846 left = Math.min(Math.max(0, left), runtime.limitCol - width); 847 848 $item.removeClass('fw-float'); 849 $item.css({ 850 zIndex: "auto", 851 top: top * cellH, 852 left: left * cellW 853 }); 854 855 //check old drag element; 856 var x, y, key, oldDropId; 857 for (y = 0; y < height; ++y) { 858 for (x = 0; x < width; ++x) { 859 key = (y + top) + "-" + (x + left); 860 oldDropId = runtime.matrix[key]; 861 if (oldDropId && oldDropId != true) { 862 $("#" + oldDropId).removeAttr("data-position"); 863 } 864 } 865 } 866 867 runtime.holes = {}; 868 869 $item.attr({ 870 "data-width": $item.width(), 871 "data-height": $item.height(), 872 "data-position": top + "-" + left 873 }); 874 875 klass.refresh(); 876 877 setting.onBlockDrop.call(item, event); 878 } 879 }); 880 } 881 882 883 $.extend(klass, { 884 885 addCustomEvent: function(name, func) { 886 var events = runtime.events; 887 name = name.toLowerCase(); 888 !events[name] && (events[name] = []); 889 func.eid = events[name].length; 890 events[name].push(func); 891 return this; 892 }, 893 894 appendBlock: function(items) { 895 var allBlock = $(items).appendTo(container); 896 var block = null; 897 var activeBlock = []; 898 899 if (runtime.arguments) { 900 901 if ($.isFunction(runtime.sortFunc)) { 902 allBlock.sort(runtime.sortFunc); 903 } 904 905 allBlock.each(function(index, item) { 906 item.index = ++index; 907 block = layoutManager.loadBlock(item, setting); 908 block && activeBlock.push(block); 909 }); 910 911 engine[setting.engine](activeBlock, setting); 912 913 layoutManager.setWallSize(runtime, container); 914 915 runtime.length = allBlock.length; 916 917 allBlock.each(function(index, item) { 918 layoutManager.showBlock(item, setting); 919 if (setting.draggable || item.getAttribute('data-draggable')) { 920 setDraggable(item); 921 } 922 }); 923 } 924 }, 925 /* 926 add one or more blank area (hole) on layout; 927 example: 928 929 wall.appendHoles({ 930 top: 10, 931 left: 36, 932 width: 2, 933 height: 6 934 }); 935 936 wall.appendHoles([ 937 { 938 top: 16, 939 left: 16, 940 width: 8, 941 height: 2 942 }, 943 { 944 top: 10, 945 left: 36, 946 width: 2, 947 height: 6 948 } 949 ]); 950 951 */ 952 appendHoles: function(holes) { 953 var newHoles = [].concat(holes), h = {}, i; 954 for (i = 0; i < newHoles.length; ++i) { 955 h = newHoles[i]; 956 runtime.holes[h.top + "-" + h.left + "-" + h.width + "-" + h.height] = h; 957 } 958 return this; 959 }, 960 961 container: container, 962 963 destroy: function() { 964 var allBlock = container.find(setting.selector).removeAttr('id'), 965 block = null, 966 activeBlock = []; 967 968 allBlock.each(function(index, item) { 969 $item = $(item); 970 var width = 1 * $item.attr('data-width') || ""; 971 var height = 1 * $item.attr('data-height') || ""; 972 $item.width(width).height(height).css({ 973 position: 'static' 974 }); 975 }); 976 }, 977 978 fillHoles: function(holes) { 979 if (arguments.length == 0) { 980 runtime.holes = {}; 981 } else { 982 var newHoles = [].concat(holes), h = {}, i; 983 for (i = 0; i < newHoles.length; ++i) { 984 h = newHoles[i]; 985 delete runtime.holes[h.top + "-" + h.left + "-" + h.width + "-" + h.height]; 986 } 987 } 988 return this; 989 }, 990 991 filter: function(filter) {
992 runtime.filter = filter; 993 if (runtime.arguments) { 994 this.refresh(); 995 } 996 return this; 997 }, 998 999 fireEvent: function(name, object, setting) { 1000 var events = runtime.events; 1001 name = name.toLowerCase(); 1002 if (events[name] && events[name].length) { 1003 for (var i = 0; i < events[name].length; ++i) { 1004 events[name][i].call(this, object, setting); 1005 } 1006 } 1007 return this; 1008 }, 1009 1010 fitHeight: function(height) { 1011 1012 var height = height ? height : container.height() || $W.height(); 1013 1014 this.fitZone('auto', height); 1015 1016 runtime.arguments = arguments; 1017 }, 1018 1019 fitWidth: function(width) { 1020 1021 var width = width ? width : container.width() || $W.width(); 1022 1023 this.fitZone(width, 'auto'); 1024 1025 runtime.arguments = arguments; 1026 }, 1027 1028 fitZone: function(width, height) { 1029 var allBlock = container.find(setting.selector).removeAttr('id'), 1030 block = null, 1031 activeBlock = []; 1032 1033 height = height ? height : container.height() || $W.height(); 1034 width = width ? width : container.width() || $W.width(); 1035 1036 runtime.arguments = arguments; 1037 1038 layoutManager.resetGrid(runtime); 1039 1040 layoutManager.adjustUnit(width, height, setting); 1041 1042 if (runtime.filter) { 1043 allBlock.data('active', 0); 1044 allBlock.filter(runtime.filter).data('active', 1); 1045 } else { 1046 allBlock.data('active', 1); 1047 } 1048 1049 if ($.isFunction(runtime.sortFunc)) { 1050 allBlock.sort(runtime.sortFunc); 1051 } 1052 1053 allBlock.each(function(index, item) { 1054 var $item = $(item); 1055 item.index = ++index; 1056 block = layoutManager.loadBlock(item, setting); 1057 block && $item.data("active") && activeBlock.push(block); 1058 }); 1059 1060 klass.fireEvent('onGridReady', container, setting); 1061 1062 engine[setting.engine](activeBlock, setting); 1063 1064 layoutManager.setWallSize(runtime, container); 1065 1066 klass.fireEvent('onGridArrange', container, setting); 1067 1068 runtime.length = allBlock.length; 1069 1070 allBlock.each(function(index, item) { 1071 layoutManager.showBlock(item, setting); 1072 if (setting.draggable || item.getAttribute('data-draggable')) { 1073 setDraggable(item); 1074 } 1075 }); 1076 }, 1077 1078 /* 1079 set block with special position, the top and left are multiple of unit width/height; 1080 example: 1081 1082 wall.fixPos({ 1083 top: 0, 1084 left: 0, 1085 block: $('.free') 1086 }); 1087 */ 1088 fixPos: function(option) { 1089 $(option.block).attr({'data-position': option.top + "-" + option.left}); 1090 return this; 1091 }, 1092 1093 /* 1094 set block with special size, the width and height are multiple of unit width/height; 1095 example: 1096 1097 wall.fixSize({ 1098 height: 5, 1099 width: 2, 1100 block: $('.free') 1101 }); 1102 */ 1103 fixSize: function(option) { 1104 option.height != null && $(option.block).attr({'data-height': option.height}); 1105 option.width != null && $(option.block).attr({'data-width': option.width}); 1106 return this; 1107 }, 1108 1109 prepend: function(items) { 1110 container.prepend(items); 1111 if (runtime.arguments) { 1112 this.refresh(); 1113 } 1114 return this; 1115 }, 1116 1117 refresh: function() { 1118 var params = arguments.length ? arguments : runtime.arguments; 1119 var oldArg = runtime.arguments; 1120 var method = oldArg ? oldArg.callee : this.fitWidth; 1121 method.apply(this, Array.prototype.slice.call(params, 0)); 1122 return this; 1123 }, 1124 1125 /* 1126 custom layout setting; 1127 example: 1128 1129 wall.reset({ 1130 selector: '.brick', 1131 animate: true, 1132 cellW: 160, 1133 cellH: 160, 1134 delay: 50, 1135 onResize: function() { 1136 wall.fitWidth(); 1137 } 1138 }); 1139 */ 1140 reset: function(option) { 1141 $.extend(setting, option); 1142 return this; 1143 }, 1144 1145 /* 1146 create one or more blank area (hole) on layout; 1147 example: 1148 1149 wall.setHoles({ 1150 top: 2, 1151 left: 2, 1152 width: 2, 1153 height: 2 1154 }); 1155 */ 1156 1157 setHoles: function(holes) { 1158 var newHoles = [].concat(holes), h = {}, i; 1159 runtime.holes = {}; 1160 for (i = 0; i < newHoles.length; ++i) { 1161 h = newHoles[i]; 1162 runtime.holes[h.top + "-" + h.left + "-" + h.width + "-" + h.height] = h; 1163 } 1164 return this; 1165 }, 1166 /* 1167 sort items by using array sort function; 1168 example: 1169 1170 wall.sortBy(function(itemA, itemB) { 1171 return $(itemA).width() - $(itemB).width(); 1172 }); 1173 */ 1174 sortBy: function(func) { 1175 runtime.sortFunc = func; 1176 if (runtime.arguments) { 1177 this.refresh(); 1178 } 1179 return this; 1180 }, 1181 1182 unFilter: function() { 1183 delete runtime.filter; 1184 this.refresh(); 1185 return this; 1186 } 1187 }); 1188 1189 container.attr('data-min-width', Math.floor($W.width() / 80) * 80); 1190 // execute plugins; 1191 for (var i in layoutManager.plugin) { 1192 if (layoutManager.plugin.hasOwnProperty(i)) { 1193 layoutManager.plugin[i].call(klass, setting, container); 1194 } 1195 } 1196 1197 // setup resize event; 1198 $W.resize(function() { 1199 if (runtime.running) return; 1200 runtime.running = 1; 1201 setTimeout(function() { 1202 runtime.running = 0; 1203 setting.onResize.call(klass, container); 1204 }, 122); 1205 container.attr('data-min-width', Math.floor($W.width() / 80) * 80); 1206 }); 1207 }; 1208 1209 /* 1210 add default setting; 1211 example: 1212 1213 Freewall.addConfig({ 1214 offsetLeft: 0 1215 }); 1216 */ 1217 Freewall.addConfig = function(newConfig) { 1218 // add default setting; 1219 $.extend(layoutManager.defaultConfig, newConfig); 1220 }; 1221 1222 1223 /* 1224 support create new arrange algorithm; 1225 example: 1226 1227 Freewall.createEngine({ 1228 slice: function(items, setting) { 1229 // slice engine; 1230 } 1231 }); 1232 */ 1233 Freewall.createEngine = function(engineData) { 1234 // create new engine; 1235 $.extend(engine, engineData); 1236 }; 1237 1238 /* 1239 support create new plugin; 1240 example: 1241 1242 Freewall.createPlugin({ 1243 centering: function(setting, container) { 1244 console.log(this); 1245 console.log(setting); 1246 } 1247 })l 1248 */ 1249 Freewall.createPlugin = function(pluginData) { 1250 // register new plugin; 1251 $.extend(layoutManager.plugin, pluginData); 1252 }; 1253 1254 /* 1255 support access helper function; 1256 example: 1257 1258 Freewall.getMethod('setBlock')(block, setting); 1259 */ 1260 Freewall.getMethod = function(method) { 1261 // get helper method; 1262 return layoutManager[method]; 1263 }; 1264 1265 window.Freewall = window.freewall = Freewall; 1266 1267})(window.Zepto || window.jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.