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