public
Description: OpenLaszlo utilities: flash bridge, ajax, etc.
Homepage:
Clone URL: git://github.com/osteele/lzosutils.git
lzosutils / lib / layout-utils.js
100644 120 lines (109 sloc) 4.415 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/* Copyright 2006-2007 by Oliver Steele. All rights reserved. */
 
var Size = function(width, height) {
    this.width = width;
    this.height = height;
}
 
Size.prototype.maxSizeTo = function(maxWidth, maxHeight) {
    arguments.length < 1 && (maxHeight = maxWidth);
    var width = Math.min(this.width, maxWidth);
    var height = Math.min(this.height, maxHeight);
    var ratio = this.width / this.height;
    if (width / height > ratio)
        width = height * ratio;
    else
        height = width / ratio;
    return new Size(width, height);
}
 
/*
* Relative Position
*/
 
LzView.prototype.moveNextTo = function(referenceView, options) {
    var position = findBestRelativePosition(this, referenceView, options),
        duration = (options||{}).duration;
    (duration
     ? this.to({x:position.x, y:position.y}, duration)
     : this.set({x:position.x, y:position.y}));
}
 
 
// view must be within canvas
 
 
function findBestRelativePosition(view, reference, options) {
    var defaults = {
        container: canvas,
        margin: 10
    };
    var options = Hash.merge(defaults, options || {}),
        container = options.container,
        margin = options.margin,
        avoid = options.avoid;
    var refBounds = reference.getAbsoluteBounds(container),
        refLeft = refBounds.x,
        refTop = refBounds.y,
        refRight = refBounds.x + refBounds.width,
        refBottom = refBounds.y + refBounds.height;
    var containerBounds = options.outerBounds || container.getAbsoluteBounds(),
        containerLeft = containerBounds.x,
        containerTop = containerBounds.y,
        containerRight = containerBounds.x + containerBounds.width,
        containerBottom = containerBounds.y + containerBounds.height;
 
    // Consider aligning the left side with the left side of the
    // container ('c'ontainer), the left side of the reference
    // ('a'ligned), and the right side of the reference
    // ('n'eighboring); similarly for left and right reversed. We
    // prefer right to left, and below to above.
    //
    // :: width, reference min/max, container min/max
    // :: -> [[x, type, penalty]]
    // each entry is [position, type, weight]
    // type is c=container | p=proximal | a=aligned
    function positions(w, r0, r1, c0, c1, m) {
        var m = margin;
        var items = [
            // aligned to the container
            [c0+m, 'c', 1], [c1-m-w, 'c', 0],
            // neighboring the reference
            [r0-m-w, 'n', 1], [r1+m, 'n', 0],
            // aligned to the (left, right) side of the reference
            [r0, 'a', 0], [r1-w, 'a', 1],
            // centered in the reference
            [r0 + (r1 - r0 - w)/2, null, 0]
        ];
        // Penalize for each pixel outside the container.
        for (var i = 0, item; item = items[i++]; ) {
            var x = item[0], p = 0;
            if (x < c0) p += c0-x;
            if (c1 < x+w) p += x+w-c1;
            item[2] += p*1000000; // 1,000,000
        }
        return items;
    }
 
    var xs = positions(view.width, refLeft, refRight, containerLeft, containerRight);
    var ys = positions(view.height, refTop, refBottom, containerTop, containerBottom);
    var best = null;
    xs.forEach(function(xr) {
        ys.forEach(function(yr) {
            var x = xr[0], y = yr[0];
            var p = xr[2] + yr[2];
            // penalize candidates that overlap the reference
            if (x < refRight && x+view.width > refLeft && y < refBottom && y+view.height > refTop)
                p += 100000; // 100,000
            // penalize by the size of the union of the rectangles
            var x0 = Math.min(x, refLeft), x1 = Math.max(x+view.width, refRight),
                y0 = Math.min(y, refTop), y1 = Math.max(y+view.height, refBottom);
            p += (x1-x0) + (y1-y0);
            // penalize intersections with siblings
            if (avoid) {
                avoid.each(function(b) {
                    if (b.x <= x+view.width && x <= b.right &&
                        b.y <= y+view.height && y <= b.bottom)
                        p += 200;
                });
            }
            // penalize catty-corners
            // if (xr[1] == 'n' && yrs[1] == 'n')
            // p += 500;
            if (p < (best.p || p+1))
                best = {x: x, y: y, p: p};
        });
    });
    //var x = best.x, y = best.y;
    return {x:best.x, y:best.y};
}