Module: WSUI

Defined in:
lib/wsui.rb

Defined Under Namespace

Classes: S

Class Method Summary collapse

Class Method Details

.start(port = 7001, html = "", fps: 5, &block) ⇒ Object



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
121
122
123
124
125
126
# File 'lib/wsui.rb', line 7

def self.start port = 7001, html = "", fps: 5, &block
  app = Module.new do
    @connections = Set.new
    @port = port
    @fps = fps
    @block = block
    def self.call env
      f = lambda do |o|
        if o.is_a? S
          {wsui_id: o.__id__, value: o.value}
        elsif o.respond_to? :each
          o.map &f
        else
          o
        end
      end
      Async::WebSocket::Adapters::Rack.open env, protocols: %w{ ws } do |connection|
        @connections << connection
        while message = connection.read
          if message.key? :id
            t = begin
              ObjectSpace._id2ref message[:id]
            rescue RangeError
              # maybe when we click too fast and the page has 'old' refs
            end
            p t
            t.click&.call t if t.respond_to? :click
          end
          t = f.call(@block.call(message)).to_json
          @connections.each do |connection|
            connection.write t
            connection.flush
          end
        end
      rescue Protocol::WebSocket::ClosedError
        p :closed
      ensure
        @connections.delete connection
      end or [200, [], [<<~HEREDOC]]
        <html>
          <head>
            <meta charset="UTF-8">
            <script src="https://github.com/processing/p5.js/releases/download/v1.4.2/p5.min.js"></script>
            <script src="https://cdn.jsdelivr.net/gh/abachman/p5.websocket/dist/p5.websocket.min.js"></script>
            <script>
              var all = null;
              var regions = [];
              var need = false;   // force draw() on click
              function setup() {
                frameRate(#{@fps});
                createCanvas(windowWidth, windowHeight);
                textAlign(CENTER, CENTER);
                connectWebsocket("ws://" + window.location.host);
              };
              function messageReceived(data) {
                // console.log(data);
                all = JSON.parse(data);
                if (need) {
                  need = false;
                  draw();
                }
              };
              function draw() {
                // console.log(all);
                clear();
                regions = [];
                let fv = function(o, left, top, width, height) {
                  if (o && o.wsui_id) {
                    regions.push({id: o.wsui_id, left: left, top: top, width: width, height: height});
                    o = o.value;
                  };
                  if (o === null) return;
                  if (Number.isFinite(o) || typeof o === "string" || o instanceof String) {
                    textSize(min(100, textSize() * min(height / textWidth("W"), width / textWidth(o))));
                    text(o, left + width / 2, top + height / 2)
                  } else o.forEach( function(e, i) {
                    fh(e, left, top + height / o.length * i, width, height / o.length);
                  } );
                };
                let fh = function(o, left, top, width, height) {
                  if (o && o.wsui_id) {
                    regions.push({id: o.wsui_id, left: left, top: top, width: width, height: height});
                    o = o.value;
                  };
                  if (o === null) return;
                  if (Number.isFinite(o) || typeof o === "string" || o instanceof String) {
                    textSize(min(100, textSize() * min(height / textWidth("W"), width / textWidth(o))));
                    text(o, left + width / 2, top + height / 2)
                  } else o.forEach( function(e, i) {
                    fv(e, left + width / o.length * i, top, width / o.length, height);
                  } );
                };
                fv(all, 0, 0, windowWidth, windowHeight);
                sendMessage({});
              };
              function mouseClicked() {
                regions.some( function(e) {
                  if (mouseX >= e.left && mouseX <= e.left + e.width && mouseY >= e.top && mouseY <= e.top + e.height) {
                    sendMessage({id: e.id});
                    need = true;
                    return true;
                  };
                } );
              };
            </script>
          </head>
          <body style="margin:0"><main></main></body>
        </html>
      HEREDOC
    end
  end
  Thread.new do
    Async do
      Falcon::Server.new(
        Falcon::Server.middleware(app),
        Async::HTTP::Endpoint.parse("http://0.0.0.0:#{port}")
      ).run
    end
  end
end