PageSourceSearch

https://www.kai-gimmler-bikes.de/js/freewall.js

js kai-gimmler-bikes.de collected 2026-10-06 21:20:32 UTC 45,671 bytes, 1,267 lines download raw bytes

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.