fontaine 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +199 -0
- data/Rakefile +1 -0
- data/fontaine.gemspec +27 -0
- data/lib/fontaine.rb +4 -0
- data/lib/fontaine/assets/fontaine.js +452 -0
- data/lib/fontaine/bootstrap.rb +48 -0
- data/lib/fontaine/canvas.rb +243 -0
- data/lib/fontaine/gradient.rb +15 -0
- data/lib/fontaine/image_data.rb +36 -0
- data/lib/fontaine/pattern.rb +9 -0
- data/lib/fontaine/version.rb +3 -0
- metadata +100 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d703f331a7703b5b79e7a0f297fb61f1dd01901c
|
4
|
+
data.tar.gz: c07f87f06bd72a41592634dcc33d5d8420eabd71
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fa08b41eb0c8bd02dc377b0e6dbf73ac11b47c33d94a3a18efda2d354a5f741578af91582e6f23b893a9961963303c90ed5fbde24c21d14544719a17cd62864f
|
7
|
+
data.tar.gz: 9f99d633d7a9d6b4d95502e580e4f511e6cc927cf4d825e8c8b7e674bbec25ea15984b658c9848cf152cc1979e37942cb786d00e50f862bd6cc4fd0a40cd260a
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 TODO: Write your name
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,199 @@
|
|
1
|
+
# Fontaine
|
2
|
+
|
3
|
+
A bridge between Ruby and HTML5 canvas **for Sinatra** using Websocket. Basically, this allows you to make <canvas> based
|
4
|
+
apps in Ruby.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'fontaine'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
Or install it yourself as:
|
17
|
+
|
18
|
+
$ gem install fontaine
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
Just put this in your pipe:
|
23
|
+
|
24
|
+
require 'sinatra/base'
|
25
|
+
require 'fontaine/canvas' #the meat of everything canvas-related
|
26
|
+
require 'fontaine/bootstrap'
|
27
|
+
require 'sinatra-websocket'
|
28
|
+
require 'haml' #not strictly necessary, but recommended
|
29
|
+
|
30
|
+
class Sample < Sinatra::Base
|
31
|
+
register Sinatra::Fontaine::Bootstrap::Assets
|
32
|
+
set :server, 'thin'
|
33
|
+
set :sockets, []
|
34
|
+
|
35
|
+
get '/' do
|
36
|
+
|
37
|
+
@canvas = Fontaine::Canvas.new("TEST_CANVAS", 500, 500, "Your browser does not support the canvas tag",
|
38
|
+
:style => "border:1px solid #000000;") do |canvas|
|
39
|
+
|
40
|
+
canvas.on_click do |x, y, button| #when there's a click, do this:
|
41
|
+
canvas.rect(x.to_i-25, y.to_i-25, 50, 50)
|
42
|
+
|
43
|
+
if button.eql? '0' #left click will give you a solid red rectangle
|
44
|
+
canvas.fill_style("#FF0000")
|
45
|
+
canvas.fill
|
46
|
+
else #right click will give you the outline of a blue rectangle
|
47
|
+
canvas.stroke_style("blue")
|
48
|
+
canvas.stroke
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
if request.websocket?
|
55
|
+
@canvas.listen(request, settings)
|
56
|
+
else
|
57
|
+
haml :index
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
## views/index.haml
|
65
|
+
|
66
|
+
%script{:src => "/javascripts/jquery-1.9.1.js", :type => "text/javascript"}
|
67
|
+
= bootstrap_fontaine
|
68
|
+
= @canvas.display
|
69
|
+
|
70
|
+
## sample_config.ru
|
71
|
+
|
72
|
+
require './sample'
|
73
|
+
run Sample
|
74
|
+
|
75
|
+
and smoke it! (After you've put jquery-1.9.1.js into /public/javascripts/)
|
76
|
+
|
77
|
+
rackup sample_config.ru
|
78
|
+
|
79
|
+
Ok, that's pretty complicated. Let me give you some further explanations. We'll start with
|
80
|
+
making a new Canvas:
|
81
|
+
|
82
|
+
Fontaine::Canvas.new("TEST_CANVAS", 500, 500, "Your browser does not support the canvas tag",
|
83
|
+
:style => "border:1px solid #000000;")
|
84
|
+
|
85
|
+
This is pretty simple. The first parameter is simply the id of the canvas you will be displaying. The second
|
86
|
+
and third parameters are the width and height of the canvas. The fourth is the alt text if the user's browser
|
87
|
+
doesn't support <canvas>. Finally, the fifth parameter is a hash of any other html options you
|
88
|
+
wish to add to the canvas. In this case, I'm giving it a border.
|
89
|
+
|
90
|
+
Next, while initializing, you can give it a block of code to execute when the user takes actions related to the canvas.
|
91
|
+
(Note, you can do this at any point once the canvas is initialized by passing a block to any of the action methods).
|
92
|
+
The block will be whatever code you want to run in response to those actions. In addition, each action is yielded
|
93
|
+
a few parameters from the javascript event call. For more information on specific actions available and their parameters, see
|
94
|
+
the **Action Methods** section.
|
95
|
+
|
96
|
+
That's only half the story, though. You've got to be able to draw on the canvas! You can use any of the methods from
|
97
|
+
[here](http://www.w3schools.com/tags/ref_canvas.asp) and they will work pretty much how you'd expect, with a couple
|
98
|
+
exceptions (see **Exceptions and Issues**). Most of the time, you simply can directly use a 'rubyfied' version of the same
|
99
|
+
method. For example, instead of calling @canvas.fillRect(), you call @canvas.fill_rect. The other major exception is that rather
|
100
|
+
than setting canvas attributes with '=', you'll simply use a method of the same name (canvas.fill_style("#FF0000"),
|
101
|
+
not canvas.fill_style = "#FF0000")
|
102
|
+
|
103
|
+
Alright, so you've got your action methods set up and you know how to draw on the canvas, you just have to get it going. In the
|
104
|
+
sinatra file this is pretty simple. When you get a request, listen! Pass in the request and the sinatra settings.
|
105
|
+
|
106
|
+
if request.websocket?
|
107
|
+
@canvas.listen(request, settings)
|
108
|
+
end
|
109
|
+
|
110
|
+
The view is almost as easy:
|
111
|
+
|
112
|
+
%script{:src => "/javascripts/jquery-1.9.1.js", :type => "text/javascript"}
|
113
|
+
= bootstrap_fontaine
|
114
|
+
= @canvas.display
|
115
|
+
|
116
|
+
Bam! You need jquery. I've tested on 1.9.1, but not any other versions. bootstrap_fontaine gives you access to the fontaine javascript
|
117
|
+
file. @canvas.display will display the canvas.
|
118
|
+
|
119
|
+
## Action Methods
|
120
|
+
|
121
|
+
**NOTE: All coordinates are in relation to the canvas, not to the document** 0, 0 is the top-left corner of the canvas, for example.
|
122
|
+
|
123
|
+
**on_click** Yields x, y, and button
|
124
|
+
|
125
|
+
**on_mousedown** Yields x, y, and button
|
126
|
+
|
127
|
+
**on_mouseup** Yields x, y, and button
|
128
|
+
|
129
|
+
**on_mouseover** Yields x and y
|
130
|
+
|
131
|
+
**on_mouseout** Yields x and y
|
132
|
+
|
133
|
+
**on_mousemove** Yields x and y
|
134
|
+
|
135
|
+
**on_keydown** Yields key_code
|
136
|
+
|
137
|
+
(Note, this is not the ascii value of the key pressed. See javascript documentation on the difference between keyCode and
|
138
|
+
the value)
|
139
|
+
|
140
|
+
**on_keyup** Yields key_code
|
141
|
+
|
142
|
+
**on_keypress** Yields which
|
143
|
+
(This *is* the ascii value of the key pressed)
|
144
|
+
|
145
|
+
## Drawing methods
|
146
|
+
|
147
|
+
You can use any of the methods from
|
148
|
+
[here](http://www.w3schools.com/tags/ref_canvas.asp) and they will work pretty much how you'd expect, with a couple
|
149
|
+
exceptions (see **Exceptions and Issues**). Most of the time, you simply can directly use a 'rubyfied' version of the same
|
150
|
+
method. For example, instead of calling @canvas.fillRect(), you call @canvas.fill_rect. The other major exception is that rather
|
151
|
+
than setting canvas attributes with '=', you'll simply use a method of the same name (canvas.fill_style("#FF0000"),
|
152
|
+
not canvas.fill_style = "#FF0000")
|
153
|
+
|
154
|
+
## Exceptions and Issues
|
155
|
+
|
156
|
+
Any "Drawing" method that returns an attribute doesn't "really" give you the attribute. It returns the value of the attribute from the
|
157
|
+
last time you changed it from the ruby Canvas object. In other words, if I call
|
158
|
+
|
159
|
+
@canvas.fill_style("blue")
|
160
|
+
|
161
|
+
on the ruby side, then later call
|
162
|
+
|
163
|
+
canvas.fillStyle = "#FF0000"
|
164
|
+
|
165
|
+
in the javascript, when I call
|
166
|
+
|
167
|
+
@canvas.fill_style
|
168
|
+
|
169
|
+
in ruby, it'll still return "blue".
|
170
|
+
|
171
|
+
Basically, this is because websocket is impatient. It doesn't have a protocol for waiting for the javascript to return information before
|
172
|
+
executing the next line of code. So, I faked it and track everything on the fontaine canvas object.
|
173
|
+
|
174
|
+
If you stick to just using the fontaine canvas methods, this mostly doesn't have an impact, **except** that the get_image_data method isn't
|
175
|
+
implemented, and doesn't work right now. Don't even try.
|
176
|
+
|
177
|
+
## Contributing
|
178
|
+
|
179
|
+
1. Fork it
|
180
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
181
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
182
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
183
|
+
5. Create new Pull Request
|
184
|
+
|
185
|
+
##Future Features/Goals
|
186
|
+
|
187
|
+
**Soon**
|
188
|
+
|
189
|
+
* Implement on_keystroke methods for each key. For example, on_keystroke_a.
|
190
|
+
|
191
|
+
**Later**
|
192
|
+
|
193
|
+
* Figure a more elegant way to bootstrap the javascript, avoiding rackup and extending canvas
|
194
|
+
* Add a scheduler or some other way to "wait" for get methods from the javascript
|
195
|
+
|
196
|
+
**In the distant future, when apes rule the Earth**
|
197
|
+
|
198
|
+
* Implement this in Ruby on Rails
|
199
|
+
* Implement audio and video tags
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/fontaine.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'fontaine/version'
|
5
|
+
require 'fontaine/canvas'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = "fontaine"
|
9
|
+
spec.version = Fontaine::VERSION
|
10
|
+
spec.authors = ["J. Paul Wetstein"]
|
11
|
+
spec.email = ["Jeep.Wetstein@gmail.com"]
|
12
|
+
spec.description = %q{A bridge between Ruby and HTML5 canvas using Websocket}
|
13
|
+
spec.summary = %q{A bridge between Ruby and HTML5 canvas using Websocket}
|
14
|
+
spec.homepage = ""
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files`.split($/)
|
18
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_dependency 'sinatra-websocket', '~>0.2.0'
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
25
|
+
spec.add_development_dependency "rake"
|
26
|
+
|
27
|
+
end
|
data/lib/fontaine.rb
ADDED
@@ -0,0 +1,452 @@
|
|
1
|
+
$(document).ready(function()
|
2
|
+
{
|
3
|
+
var ws = new WebSocket('ws://' + window.location.host + window.location.pathname);
|
4
|
+
|
5
|
+
var canvas;
|
6
|
+
var ctx;
|
7
|
+
var canvasPosition;
|
8
|
+
var current_mouse;
|
9
|
+
var objectsHash = {};
|
10
|
+
|
11
|
+
ws.onmessage = function(msg)
|
12
|
+
{
|
13
|
+
msg_array = msg.data.split(" ");
|
14
|
+
command = msg_array[0];
|
15
|
+
msg_array.shift();
|
16
|
+
params = msg_array;
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
switch(command)
|
21
|
+
{
|
22
|
+
|
23
|
+
case "register":
|
24
|
+
canvas = $(params[0]);
|
25
|
+
ctx=canvas[0].getContext('2d');
|
26
|
+
canvasPosition =
|
27
|
+
{
|
28
|
+
x: canvas.offset().left,
|
29
|
+
y: canvas.offset().top
|
30
|
+
};
|
31
|
+
|
32
|
+
canvas.on('mousedown', function(e)
|
33
|
+
{
|
34
|
+
current_mouse =
|
35
|
+
{
|
36
|
+
x: e.pageX - canvasPosition.x,
|
37
|
+
y: e.pageY - canvasPosition.y
|
38
|
+
}
|
39
|
+
ws.send("mousedown x "+current_mouse.x+" y "+current_mouse.y + " button "+ e.button);
|
40
|
+
});
|
41
|
+
|
42
|
+
canvas.on('mouseup', function(e)
|
43
|
+
{
|
44
|
+
current_mouse =
|
45
|
+
{
|
46
|
+
x: e.pageX - canvasPosition.x,
|
47
|
+
y: e.pageY - canvasPosition.y
|
48
|
+
}
|
49
|
+
ws.send("mouseup x "+current_mouse.x+" y "+current_mouse.y + " button "+ e.button);
|
50
|
+
});
|
51
|
+
|
52
|
+
canvas.on('click', function(e)
|
53
|
+
{
|
54
|
+
e.preventDefault();
|
55
|
+
current_mouse =
|
56
|
+
{
|
57
|
+
x: e.pageX - canvasPosition.x,
|
58
|
+
y: e.pageY - canvasPosition.y
|
59
|
+
}
|
60
|
+
ws.send("click x "+current_mouse.x+" y "+current_mouse.y + " button "+ e.button);
|
61
|
+
});
|
62
|
+
|
63
|
+
canvas.on('mousemove', function(e)
|
64
|
+
{
|
65
|
+
current_mouse =
|
66
|
+
{
|
67
|
+
x: e.pageX - canvasPosition.x,
|
68
|
+
y: e.pageY - canvasPosition.y
|
69
|
+
}
|
70
|
+
ws.send("mousemove x "+current_mouse.x+" y "+current_mouse.y + " button "+ e.button);
|
71
|
+
});
|
72
|
+
|
73
|
+
canvas.on('mouseover', function(e)
|
74
|
+
{
|
75
|
+
current_mouse =
|
76
|
+
{
|
77
|
+
x: e.pageX - canvasPosition.x,
|
78
|
+
y: e.pageY - canvasPosition.y
|
79
|
+
}
|
80
|
+
ws.send("mouseover x "+current_mouse.x+" y "+current_mouse.y + " button "+ e.button);
|
81
|
+
});
|
82
|
+
|
83
|
+
canvas.on('mouseout', function(e)
|
84
|
+
{
|
85
|
+
current_mouse =
|
86
|
+
{
|
87
|
+
x: e.pageX - canvasPosition.x,
|
88
|
+
y: e.pageY - canvasPosition.y
|
89
|
+
}
|
90
|
+
ws.send("mouseout x "+current_mouse.x+" y "+current_mouse.y + " button "+ e.button);
|
91
|
+
});
|
92
|
+
|
93
|
+
// canvas.on('touchstart', function(e)
|
94
|
+
// {
|
95
|
+
// e.preventDefault();
|
96
|
+
// var touchList = e.changedTouches;
|
97
|
+
// var current_touch;
|
98
|
+
// for(var i = 0; i < touchList.length; i++)
|
99
|
+
// {
|
100
|
+
// current_touch =
|
101
|
+
// {
|
102
|
+
// x: touchList[i].screenX - canvasPosition.x,
|
103
|
+
// y: touchList[i].screenY - canvasPosition.y,
|
104
|
+
// id: touchList[i].identifier
|
105
|
+
// };
|
106
|
+
// ws.send("touchstart x "+current_touch.x+" y "+current_touch.y + " id " + current_touch.id);
|
107
|
+
// }
|
108
|
+
// });
|
109
|
+
//
|
110
|
+
// canvas.on('touchmove', function(e)
|
111
|
+
// {
|
112
|
+
// var touchList = e.changedTouches;
|
113
|
+
// var current_touch;
|
114
|
+
// for(var i = 0; i < touchList.length; i++)
|
115
|
+
// {
|
116
|
+
// current_touch =
|
117
|
+
// {
|
118
|
+
// x: touchList[i].screenX - canvasPosition.x,
|
119
|
+
// y: touchList[i].screenY - canvasPosition.y,
|
120
|
+
// id: touchList[i].identifier
|
121
|
+
// };
|
122
|
+
// ws.send("touchmove x "+current_touch.x+" y "+current_touch.y + " id " + current_touch.id);
|
123
|
+
// }
|
124
|
+
// });
|
125
|
+
//
|
126
|
+
// canvas.on('touchend', function(e)
|
127
|
+
// {
|
128
|
+
// var touchList = e.changedTouches;
|
129
|
+
// var current_touch;
|
130
|
+
// for(var i = 0; i < touchList.length; i++)
|
131
|
+
// {
|
132
|
+
// current_touch =
|
133
|
+
// {
|
134
|
+
// x: touchList[i].screenX - canvasPosition.x,
|
135
|
+
// y: touchList[i].screenY - canvasPosition.y,
|
136
|
+
// id: touchList[i].identifier
|
137
|
+
// };
|
138
|
+
// ws.send("touchend x "+current_touch.x+" y "+current_touch.y + " id " + current_touch.id);
|
139
|
+
// }
|
140
|
+
// });
|
141
|
+
|
142
|
+
document.onkeydown = function(e)
|
143
|
+
{
|
144
|
+
ws.send("keydown key_code "+e.keyCode); //+ " which " + e.which + " char_code " + e.charCode);
|
145
|
+
}
|
146
|
+
|
147
|
+
document.onkeypress = function(e)
|
148
|
+
{
|
149
|
+
ws.send("keypress which "+e.which); //+ " which " + e.which + " char_code " + e.charCode);
|
150
|
+
}
|
151
|
+
|
152
|
+
document.onkeyup = function(e)
|
153
|
+
{
|
154
|
+
ws.send("keyup key_code "+e.keyCode); //+ " which " + e.which + " char_code " + e.charCode);
|
155
|
+
}
|
156
|
+
|
157
|
+
break;
|
158
|
+
case "fillStyle":
|
159
|
+
if(params.length==1)
|
160
|
+
{
|
161
|
+
ctx.fillStyle = params[0];
|
162
|
+
}
|
163
|
+
ws.send("response " + ctx.fillStyle);
|
164
|
+
break;
|
165
|
+
case 'fillStyleObject':
|
166
|
+
ctx.fillStyle = objectsHash[params[0]];
|
167
|
+
ws.send("response " + ctx.fillStyle);
|
168
|
+
break;
|
169
|
+
case "strokeStyle":
|
170
|
+
if(params.length>0)
|
171
|
+
{
|
172
|
+
ctx.strokeStyle = params[0];
|
173
|
+
}
|
174
|
+
ws.send("response " + ctx.strokeStyle);
|
175
|
+
break;
|
176
|
+
case 'strokeStyleObject':
|
177
|
+
ctx.strokeStyle = objectsHash[params[0]];
|
178
|
+
ws.send("response " + ctx.strokeStyle);
|
179
|
+
break;
|
180
|
+
case "shadowColor":
|
181
|
+
if(params[0] != "")
|
182
|
+
{
|
183
|
+
ctx.shadowColor = params[0];
|
184
|
+
}
|
185
|
+
//alert("sc " + ctx.shadowColor);
|
186
|
+
ws.send("response " + ctx.shadowColor);
|
187
|
+
break;
|
188
|
+
case "shadowBlur":
|
189
|
+
if(params.length>0)
|
190
|
+
{
|
191
|
+
ctx.shadowBlur = params[0];
|
192
|
+
}
|
193
|
+
ws.send("response " + ctx.shadowBlur);
|
194
|
+
break;
|
195
|
+
case "shadowOffsetX":
|
196
|
+
if(params.length>0)
|
197
|
+
{
|
198
|
+
ctx.shadowOffsetX = params[0];
|
199
|
+
}
|
200
|
+
ws.send("response " + ctx.shadowOffsetX);
|
201
|
+
break;
|
202
|
+
case "shadowOffsetY":
|
203
|
+
if(params.length>0)
|
204
|
+
{
|
205
|
+
ctx.shadowOffsetY = params[0];
|
206
|
+
}
|
207
|
+
ws.send("response " + ctx.shadowOffsetY);
|
208
|
+
break;
|
209
|
+
|
210
|
+
case "createLinearGradient":
|
211
|
+
objectsHash[params[4]] =
|
212
|
+
ctx.createLinearGradient(params[0], params[1], params[2], params[3]);
|
213
|
+
break;
|
214
|
+
case "createPattern":
|
215
|
+
objectsHash[params[2]] = ctx.createPattern(params[0], params[1]);
|
216
|
+
break;
|
217
|
+
case "createRadialGradient":
|
218
|
+
objectsHash[params[6]] =
|
219
|
+
ctx.createRadialGradient(params[0], params[1], params[2], params[3], params[4], params[5]);
|
220
|
+
break;
|
221
|
+
case "addColorStop":
|
222
|
+
objectsHash[params[2].addColorStop(params[0], params[1])];
|
223
|
+
break;
|
224
|
+
case "lineCap":
|
225
|
+
if(params.length>0)
|
226
|
+
{
|
227
|
+
ctx.lineCap = params[0]
|
228
|
+
}
|
229
|
+
ws.send("response " + ctx.lineCap);
|
230
|
+
break;
|
231
|
+
case "lineJoin":
|
232
|
+
if(params.length>0)
|
233
|
+
{
|
234
|
+
ctx.lineJoin = params[0]
|
235
|
+
}
|
236
|
+
ws.send("response " + ctx.lineJoin);
|
237
|
+
break;
|
238
|
+
case "lineWidth":
|
239
|
+
if(params.length>0)
|
240
|
+
{
|
241
|
+
ctx.lineWidth = params[0]
|
242
|
+
}
|
243
|
+
ws.send("response " + ctx.lineWidth);
|
244
|
+
break;
|
245
|
+
case "miterLimit":
|
246
|
+
if(params.length>0)
|
247
|
+
{
|
248
|
+
ctx.miterLimit = params[0]
|
249
|
+
}
|
250
|
+
ws.send("response " + ctx.miterLimit);
|
251
|
+
break;
|
252
|
+
case "rect":
|
253
|
+
ctx.rect(params[0], params[1], params[2], params[3]);
|
254
|
+
break;
|
255
|
+
case "strokeRect":
|
256
|
+
ctx.strokeRect(params[0], params[1], params[2], params[3]);
|
257
|
+
break;
|
258
|
+
case "fillRect":
|
259
|
+
ctx.fillRect(params[0], params[1], params[2], params[3]);
|
260
|
+
break;
|
261
|
+
case "clearRect":
|
262
|
+
ctx.clearRect(params[0], params[1], params[2], params[3]);
|
263
|
+
break;
|
264
|
+
case "fill":
|
265
|
+
ctx.fill();
|
266
|
+
break;
|
267
|
+
case "stroke":
|
268
|
+
ctx.stroke();
|
269
|
+
break;
|
270
|
+
case "beginPath":
|
271
|
+
ctx.beginPath();
|
272
|
+
break;
|
273
|
+
case "moveTo":
|
274
|
+
ctx.moveTo(params[0], params[1]);
|
275
|
+
break;
|
276
|
+
case "closePath":
|
277
|
+
ctx.closePath();
|
278
|
+
break;
|
279
|
+
case "lineTo":
|
280
|
+
ctx.lineTo(params[0], params[1]);
|
281
|
+
break;
|
282
|
+
case "clip":
|
283
|
+
ctx.clip();
|
284
|
+
break;
|
285
|
+
case "quadraticCurveTo":
|
286
|
+
ctx.quadraticCurveTo(params[0], params[1], params[2], params[3]);
|
287
|
+
break;
|
288
|
+
case "bezierCurveTo":
|
289
|
+
ctx.bezierCurveTo(params[0], params[1], params[2], params[3], params[4], params[5]);
|
290
|
+
break;
|
291
|
+
case "arc":
|
292
|
+
ctx.arc(params[0], params[1], params[2], params[3], params[4], params[5]);
|
293
|
+
break;
|
294
|
+
case "arcTo":
|
295
|
+
ctx.arcTo(params[0], params[1], params[2], params[3], params[4]);
|
296
|
+
break;
|
297
|
+
case "isPointInPath":
|
298
|
+
ctx.isPointInPath(params[0], params[1]);
|
299
|
+
break;
|
300
|
+
case "scale":
|
301
|
+
ctx.scale(params[0], params[1]);
|
302
|
+
break;
|
303
|
+
case "rotate":
|
304
|
+
ctx.rotate(params[0]);
|
305
|
+
break;
|
306
|
+
case "translate":
|
307
|
+
ctx.translate(params[0], params[1]);
|
308
|
+
break;
|
309
|
+
case "transform":
|
310
|
+
ctx.transform(params[0], params[1], params[2], params[3], params[4], params[5]);
|
311
|
+
break;
|
312
|
+
case "setTransform":
|
313
|
+
ctx.setTransform(params[0], params[1], params[2], params[3], params[4], params[5]);
|
314
|
+
break;
|
315
|
+
case "font":
|
316
|
+
if(params.length!=0)
|
317
|
+
{
|
318
|
+
ctx.font = params.join(" ");
|
319
|
+
}
|
320
|
+
ws.send("response " + ctx.font);
|
321
|
+
break;
|
322
|
+
case "textAlign":
|
323
|
+
if(params.length!=0)
|
324
|
+
{
|
325
|
+
ctx.textAlign = params[0];
|
326
|
+
}
|
327
|
+
ws.send("response " + ctx.textAlign);
|
328
|
+
break;
|
329
|
+
case "textBaseline":
|
330
|
+
if(params.length!=0)
|
331
|
+
{
|
332
|
+
ctx.textBaseline = params[0];
|
333
|
+
}
|
334
|
+
ws.send("response " + ctx.textBaseline);
|
335
|
+
break;
|
336
|
+
case "fillText":
|
337
|
+
if(params.length==3)
|
338
|
+
{
|
339
|
+
ctx.fillText(params[0], params[1], params[2]);
|
340
|
+
}
|
341
|
+
else
|
342
|
+
{
|
343
|
+
ctx.fillText(params[0], params[1], params[2], params[3]);
|
344
|
+
}
|
345
|
+
break;
|
346
|
+
case "strokeText":
|
347
|
+
if(params.length==3)
|
348
|
+
{
|
349
|
+
ctx.strokeText(params[0], params[1], params[2]);
|
350
|
+
}
|
351
|
+
else
|
352
|
+
{
|
353
|
+
ctx.strokeText(params[0], params[1], params[2], params[3]);
|
354
|
+
}
|
355
|
+
break;
|
356
|
+
case "measureText":
|
357
|
+
ws.send("response " + ctx.measureText(params[0]).width);
|
358
|
+
break;
|
359
|
+
case "drawImage":
|
360
|
+
var img=document.getElementById(params[0]);
|
361
|
+
if(params.length == 3)
|
362
|
+
{
|
363
|
+
ctx.drawImage(img, params[1], params[2]);
|
364
|
+
}
|
365
|
+
else if(params.length == 5)
|
366
|
+
{
|
367
|
+
ctx.drawImage(img, params[1], params[2], params[3], params[4]);
|
368
|
+
}
|
369
|
+
else
|
370
|
+
{
|
371
|
+
ctx.drawImage(img, params[1], params[2], params[3], params[4], params[5], params[6]);
|
372
|
+
}
|
373
|
+
break;
|
374
|
+
case "imageDataWidth":
|
375
|
+
var img=objectsHash[params[0]];
|
376
|
+
ws.send("response " + img.width);
|
377
|
+
break;
|
378
|
+
case "imageDataHeight":
|
379
|
+
var img=objectsHash[params[0]];
|
380
|
+
ws.send("response " + img.height);
|
381
|
+
break;
|
382
|
+
case "imageDataData":
|
383
|
+
var img=objectsHash[params[0]];
|
384
|
+
ws.send("response " + img.data);
|
385
|
+
break;
|
386
|
+
case "createImageData":
|
387
|
+
var img;
|
388
|
+
if(params.length == 3)
|
389
|
+
{
|
390
|
+
img = ctx.createImageData(params[0], params[1]);
|
391
|
+
}
|
392
|
+
else if(params.length == 2)
|
393
|
+
{
|
394
|
+
|
395
|
+
img = ctx.createImageData(objectsHash[params[0]]);
|
396
|
+
}
|
397
|
+
objectsHash[params[params.length-1]] = img;
|
398
|
+
break;
|
399
|
+
case "getImageData":
|
400
|
+
var img;
|
401
|
+
img = ctx.getImageData(params[0], params[1], params[2], params[3])
|
402
|
+
objectsHash[params[4]] = img;
|
403
|
+
break;
|
404
|
+
case "putImageData":
|
405
|
+
var img = objectsHash[params[0]];
|
406
|
+
if(params.length == 3)
|
407
|
+
{
|
408
|
+
ctx.putImageData(img, params[1], params[2]);
|
409
|
+
}
|
410
|
+
else if(params.length == 5)
|
411
|
+
{
|
412
|
+
ctx.putImageData(img, params[1], params[2], params[3], params[4]);
|
413
|
+
}
|
414
|
+
else if(params.length == 7)
|
415
|
+
{
|
416
|
+
ctx.putImageData(img, params[1], params[2], params[3], params[4], params[5], params[6]);
|
417
|
+
}
|
418
|
+
break;
|
419
|
+
case "globalAlpha":
|
420
|
+
if(params.length!=0)
|
421
|
+
{
|
422
|
+
ctx.globalAlpha = params[0];
|
423
|
+
}
|
424
|
+
ws.send("response " + ctx.globalAlpha);
|
425
|
+
break;
|
426
|
+
case "globalCompositeOperation":
|
427
|
+
if(params.length!=0)
|
428
|
+
{
|
429
|
+
ctx.globalCompositeOperation = params[0];
|
430
|
+
}
|
431
|
+
ws.send("response " + ctx.globalCompositeOperation);
|
432
|
+
break;
|
433
|
+
case "save":
|
434
|
+
ctx.save();
|
435
|
+
break;
|
436
|
+
case "restore":
|
437
|
+
ctx.restore();
|
438
|
+
break;
|
439
|
+
case "toDataUrl":
|
440
|
+
ws.send("response " + ctx.toDataURL());
|
441
|
+
break;
|
442
|
+
default:
|
443
|
+
ws.send("Unimplemented command");
|
444
|
+
break;
|
445
|
+
|
446
|
+
}
|
447
|
+
}
|
448
|
+
|
449
|
+
|
450
|
+
|
451
|
+
|
452
|
+
});
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Sinatra
|
2
|
+
module Fontaine
|
3
|
+
module Bootstrap
|
4
|
+
|
5
|
+
module Assets
|
6
|
+
|
7
|
+
ASSETS = {
|
8
|
+
:js => [
|
9
|
+
'fontaine.js'
|
10
|
+
],
|
11
|
+
}
|
12
|
+
|
13
|
+
def self.generate_bootstrap_asset_routes(app)
|
14
|
+
ASSETS.each do |kind, files|
|
15
|
+
files.each do |file|
|
16
|
+
name = file
|
17
|
+
|
18
|
+
app.get "/#{kind.to_s}/#{name}", :provides => kind do
|
19
|
+
File.read(File.join(File.dirname(__FILE__), 'assets', name))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.registered(app)
|
26
|
+
generate_bootstrap_asset_routes(app)
|
27
|
+
app.helpers AssetsHelper
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
module AssetsHelper
|
33
|
+
|
34
|
+
def bootstrap_fontaine
|
35
|
+
bootstrap_js
|
36
|
+
end
|
37
|
+
|
38
|
+
def bootstrap_js
|
39
|
+
output = ''
|
40
|
+
Assets::ASSETS[:js].each do |file, _|
|
41
|
+
output += '<script type="text/javascript" src="%s"></script>' % url('/js/%s' % file)
|
42
|
+
end
|
43
|
+
output
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,243 @@
|
|
1
|
+
module Fontaine
|
2
|
+
class Canvas
|
3
|
+
attr_accessor :id
|
4
|
+
attr_accessor :width
|
5
|
+
attr_accessor :height
|
6
|
+
attr_accessor :alt
|
7
|
+
attr_accessor :html_options
|
8
|
+
attr_accessor :settings
|
9
|
+
attr_accessor :ws
|
10
|
+
attr_accessor :last_response
|
11
|
+
attr_accessor :attributes
|
12
|
+
|
13
|
+
|
14
|
+
def initialize(id, width, height, alt = "", html_options = {})
|
15
|
+
@id = id
|
16
|
+
@width = width
|
17
|
+
@height = height
|
18
|
+
@alt = alt
|
19
|
+
@html_options = html_options
|
20
|
+
@attributes = {
|
21
|
+
#set the initial styles
|
22
|
+
:fill_style => "#000000",
|
23
|
+
:stroke_style => '#000000',
|
24
|
+
:shadow_color => '#000000',
|
25
|
+
:shadow_blur => 0,
|
26
|
+
:shadow_offset_x => 0,
|
27
|
+
:shadow_offset_y => 0,
|
28
|
+
:line_cap => 'butt',
|
29
|
+
:line_join => 'miter',
|
30
|
+
:line_width => '1',
|
31
|
+
:miter_limit => '10',
|
32
|
+
:font => '10px sans-serif',
|
33
|
+
:text_align => 'start',
|
34
|
+
:text_baseline => 'alphabetic',
|
35
|
+
:global_alpha => 1.0,
|
36
|
+
:global_composite_operation => 'source-over'
|
37
|
+
|
38
|
+
}
|
39
|
+
yield self if block_given?
|
40
|
+
end
|
41
|
+
|
42
|
+
def listen(request, settings)
|
43
|
+
@settings = settings
|
44
|
+
request.websocket do |ws|
|
45
|
+
ws.onopen do
|
46
|
+
@ws = ws
|
47
|
+
@ws.send("register ##{id}")
|
48
|
+
@settings.sockets << ws
|
49
|
+
end
|
50
|
+
ws.onmessage do |msg|
|
51
|
+
#puts("recieved message: #{msg}")
|
52
|
+
#EM.next_tick { process_message(msg)}
|
53
|
+
process_message(msg)
|
54
|
+
end
|
55
|
+
ws.onclose do
|
56
|
+
@settings.sockets.delete(ws)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def process_message(s_message)
|
62
|
+
a_message = s_message.split(' ')
|
63
|
+
command = a_message[0] #the first part of the message is the command
|
64
|
+
params = a_message[1..(a_message.length-1)] #get the rest of the message
|
65
|
+
params = Hash[*params] if command != "response"#convert the array to a hash if applicable
|
66
|
+
respond_to(command, params)
|
67
|
+
end
|
68
|
+
|
69
|
+
def respond_to(s_command, params = {})
|
70
|
+
case s_command
|
71
|
+
when "mousedown"
|
72
|
+
trigger_on_mousedown(params["x"], params["y"], params["button"])
|
73
|
+
when "mouseup"
|
74
|
+
trigger_on_mouseup(params["x"], params["y"], params["button"])
|
75
|
+
when "mouseover"
|
76
|
+
trigger_on_mouseover(params["x"], params["y"])
|
77
|
+
when "mouseout"
|
78
|
+
trigger_on_mouseout(params["x"], params["y"])
|
79
|
+
when "mousemove"
|
80
|
+
trigger_on_mousemove(params["x"], params["y"])
|
81
|
+
|
82
|
+
##Very glitchy right now. Need to fix this some time
|
83
|
+
|
84
|
+
# when "touchstart"
|
85
|
+
# trigger_on_touchstart(params["x"], params["y"], params["id"])
|
86
|
+
# when "touchmove"
|
87
|
+
# trigger_on_touchstart(params["x"], params["y"], params["id"])
|
88
|
+
# when "touchend"
|
89
|
+
# trigger_on_touchstart(params["x"], params["y"], params["id"])
|
90
|
+
when "keyup"
|
91
|
+
trigger_on_keyup(params["key_code"])
|
92
|
+
when "keydown"
|
93
|
+
trigger_on_keydown(params["key_code"])
|
94
|
+
when "keypress"
|
95
|
+
trigger_on_keypress(params["which"])
|
96
|
+
when "click"
|
97
|
+
trigger_on_click(params["x"], params["y"], params["button"])
|
98
|
+
when "response"
|
99
|
+
@last_response = params.flatten[0].to_s
|
100
|
+
trigger_on_response(@last_response)
|
101
|
+
else
|
102
|
+
puts "unimplemented method #{s_command}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def display
|
107
|
+
options = ""
|
108
|
+
@html_options.each_pair do |key, value|
|
109
|
+
options << "#{key}=\"#{value}\" "
|
110
|
+
end
|
111
|
+
return "<canvas id=\"#{@id}\" width=\"#{@width}\" height=\"#{@height}\" #{options}>#{@alt}</canvas>"
|
112
|
+
end
|
113
|
+
|
114
|
+
def fill_style(style="")
|
115
|
+
@attributes[:fill_style] = style if style != ""
|
116
|
+
if style.is_a? String
|
117
|
+
send_msg "fillStyle #{style}"
|
118
|
+
else
|
119
|
+
send_msg "fillStyleObject #{style.id}"
|
120
|
+
end
|
121
|
+
|
122
|
+
# return @last_response
|
123
|
+
return @attributes[:fill_style]
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
def stroke_style(style="")
|
128
|
+
@attributes[:stroke_style] = style if style != ""
|
129
|
+
if style.is_a? String
|
130
|
+
send_msg "strokeStyle #{style}"
|
131
|
+
else
|
132
|
+
send_msg "strokeStyleObject #{style.id}"
|
133
|
+
end
|
134
|
+
# return @last_response
|
135
|
+
return @attributes[:stroke_style]
|
136
|
+
end
|
137
|
+
|
138
|
+
def create_linear_gradient(x0, y0, x1, y1, id)
|
139
|
+
send_msg("createLinearGradient #{x0} #{y0} #{x1} #{y1} #{id}")
|
140
|
+
return Gradient.new(id, self)
|
141
|
+
end
|
142
|
+
|
143
|
+
def create_pattern(image, pattern)
|
144
|
+
send_msg("createPattern #{image.id} #{pattern}")
|
145
|
+
return Pattern.new(id)
|
146
|
+
end
|
147
|
+
|
148
|
+
def create_radial_gradient(x0, y0, r0, x1, y1, r1, id)
|
149
|
+
send_msg("createRadialGradient #{x0} #{y0} #{r0} #{x1} #{y1} #{r1} #{id}")
|
150
|
+
return Gradient.new(id, self)
|
151
|
+
end
|
152
|
+
|
153
|
+
def create_image_data(id, *args)
|
154
|
+
return image_data.new(id, self, args)
|
155
|
+
end
|
156
|
+
|
157
|
+
# def get_image_data(id, x, y, width, height)
|
158
|
+
# send_msg("getImageData #{x} #{y} #{width} #{height} #{id}")
|
159
|
+
# return image_data.new(id, self)
|
160
|
+
# end
|
161
|
+
|
162
|
+
def put_image_data(image, x, y, dirty_x="", dirty_y="", dirty_width="", dirty_height="")
|
163
|
+
send_msg("putImageData #{image.id} #{x} #{y} #{dirty_x} #{dirty_y} #{dirty_width} #{dirty_height}")
|
164
|
+
end
|
165
|
+
|
166
|
+
def send_msg(msg)
|
167
|
+
#puts "Sending message: #{msg}"
|
168
|
+
#EM.next_tick {@ws.send(msg)} if defined? @ws
|
169
|
+
@ws.send(msg) if defined? @ws
|
170
|
+
end
|
171
|
+
|
172
|
+
def method_missing(method_sym, *arguments, &block)
|
173
|
+
if canvas_array.include? method_sym.to_s.sub(/trigger_on_/, "")
|
174
|
+
variable_name = method_sym.to_s.sub(/trigger_/, "@")
|
175
|
+
instance_variable_get(variable_name).call(arguments) if instance_variable_defined?(variable_name)
|
176
|
+
elsif canvas_array.include? method_sym.to_s.sub(/on_/, "")
|
177
|
+
instance_variable_set("@#{method_sym}", block)
|
178
|
+
elsif draw_method_array.include? method_sym.to_s
|
179
|
+
send_msg "#{ruby_to_js_command(method_sym)} #{arguments.join(" ")}"
|
180
|
+
elsif return_method_array.include? method_sym.to_s
|
181
|
+
send_msg "#{ruby_to_js_command(method_sym)} #{arguments.join(" ")}"
|
182
|
+
@attributes[method_sym] = arguments[0] if (!arguments[0].nil? && !@attributes[method_sym].nil?)
|
183
|
+
return @attributes[method_sym]
|
184
|
+
else
|
185
|
+
super(method_sym, *arguments, &block)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def canvas_array
|
190
|
+
return["response", "mousedown", "mouseup", "keyup", "keydown", "keypress", "click", "mouseover",
|
191
|
+
"mouseout", "mousemove", "touchstart", "touchmove", "touchend"]
|
192
|
+
end
|
193
|
+
|
194
|
+
def return_method_array
|
195
|
+
return[
|
196
|
+
#Colors, Styles, and Shadows
|
197
|
+
"shadow_color", "shadow_blur", "shadow_offset_x", "shadow_offset_y",
|
198
|
+
#Line Styles
|
199
|
+
"line_cap", "line_join", "line_width", "miter_limit",
|
200
|
+
#Paths
|
201
|
+
"isPointInPath",
|
202
|
+
#Text
|
203
|
+
"font", "text_align", "text_baseline",
|
204
|
+
#Composting
|
205
|
+
"global_alpha", "global_composite_operation",
|
206
|
+
#Other
|
207
|
+
"to_data_url"
|
208
|
+
]
|
209
|
+
end
|
210
|
+
|
211
|
+
def draw_method_array
|
212
|
+
return [
|
213
|
+
#Rectangles
|
214
|
+
"rect", "fill_rect", "stroke_rect", "clear_rect",
|
215
|
+
#Paths
|
216
|
+
"fill", "stroke", "begin_path", "move_to", "close_path", "line_to", "clip",
|
217
|
+
"quadratic_curve_to", "bezier_curve_to", "arc", "arc_to",
|
218
|
+
#Transformations
|
219
|
+
"scale", "rotate", "translate", "transform", "set_transform",
|
220
|
+
#Text
|
221
|
+
"fill_text", "stroke_text", "measure_text",
|
222
|
+
#Image Drawing
|
223
|
+
"draw_image",
|
224
|
+
#Other
|
225
|
+
"save", "restore"
|
226
|
+
]
|
227
|
+
end
|
228
|
+
|
229
|
+
def ruby_to_js_command(method_sym)
|
230
|
+
js_command = method_sym.to_s
|
231
|
+
js_commands = js_command.split('_')
|
232
|
+
if js_commands.size > 1
|
233
|
+
js_commands[1..js_commands.size].each do |command|
|
234
|
+
command.capitalize!
|
235
|
+
end
|
236
|
+
end
|
237
|
+
js_commands.join
|
238
|
+
end
|
239
|
+
|
240
|
+
|
241
|
+
|
242
|
+
end
|
243
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Fontaine
|
2
|
+
class Gradient
|
3
|
+
attr_accessor :id
|
4
|
+
attr_accessor :canvas
|
5
|
+
def initialize(id, canvas)
|
6
|
+
@id = id
|
7
|
+
@canvas = canvas #is this ok? test
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_color_stop(stop, color)
|
11
|
+
@canvas.send_msg("addColorStop #{stop} #{color} #{@id}")
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Fontaine
|
2
|
+
class ImageData
|
3
|
+
attr_accessor :id
|
4
|
+
attr_accessor :canvas
|
5
|
+
|
6
|
+
def initialize(id, canvas, *args)
|
7
|
+
@id = id
|
8
|
+
@canvas = canvas #is this ok? test
|
9
|
+
if(args.size == 2)
|
10
|
+
|
11
|
+
@canvas.send_msg("createImageData #{args[0]} #{args[1]} #{id}")
|
12
|
+
|
13
|
+
elsif(args.size == 1)
|
14
|
+
@canvas.send_msg("createImageData #{args[0]} #{id}")
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
def data(pixel, value="")
|
20
|
+
@canvas.send_msg("imageDataData #{id} #{pixel} #{value}")
|
21
|
+
return @canvas.last_response
|
22
|
+
end
|
23
|
+
|
24
|
+
def width
|
25
|
+
@canvas.send_msg("imageDataWidth #{id}")
|
26
|
+
return @canvas.last_response
|
27
|
+
end
|
28
|
+
|
29
|
+
def width
|
30
|
+
@canvas.send_msg("imageDataheight #{id}")
|
31
|
+
return @canvas.last_response
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
metadata
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fontaine
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- J. Paul Wetstein
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-08-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: sinatra-websocket
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.2.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.2.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.3'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: A bridge between Ruby and HTML5 canvas using Websocket
|
56
|
+
email:
|
57
|
+
- Jeep.Wetstein@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- .gitignore
|
63
|
+
- Gemfile
|
64
|
+
- LICENSE.txt
|
65
|
+
- README.md
|
66
|
+
- Rakefile
|
67
|
+
- fontaine.gemspec
|
68
|
+
- lib/fontaine.rb
|
69
|
+
- lib/fontaine/assets/fontaine.js
|
70
|
+
- lib/fontaine/bootstrap.rb
|
71
|
+
- lib/fontaine/canvas.rb
|
72
|
+
- lib/fontaine/gradient.rb
|
73
|
+
- lib/fontaine/image_data.rb
|
74
|
+
- lib/fontaine/pattern.rb
|
75
|
+
- lib/fontaine/version.rb
|
76
|
+
homepage: ''
|
77
|
+
licenses:
|
78
|
+
- MIT
|
79
|
+
metadata: {}
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - '>='
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
requirements: []
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 2.0.3
|
97
|
+
signing_key:
|
98
|
+
specification_version: 4
|
99
|
+
summary: A bridge between Ruby and HTML5 canvas using Websocket
|
100
|
+
test_files: []
|