entityjs 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -1
- data/README.md +50 -13
- data/entityjs.gemspec +1 -1
- data/lib/entityjs/assets.rb +68 -36
- data/lib/entityjs/command.rb +3 -0
- data/lib/entityjs/commands/build.rb +58 -29
- data/lib/entityjs/commands/release.rb +42 -0
- data/lib/entityjs/parsers/parse_tmx.rb +9 -9
- data/lib/entityjs/parsers/parse_xml.rb +13 -2
- data/lib/entityjs/version.rb +1 -1
- data/lib/entityjs.rb +1 -1
- data/public/play.html +1 -1
- data/public/tests.html +1 -0
- data/spec/javascripts/helpers/canvas.js +3 -0
- data/spec/javascripts/helpers/entityunit.js +7 -0
- data/spec/javascripts/helpers/flashcanvas.js~ +28 -0
- data/spec/javascripts/helpers/soundmanager2/soundmanager2.js +5021 -0
- data/spec/javascripts/helpers/soundmanager2/soundmanager2.js~ +5021 -0
- data/spec/javascripts/helpers/soundmanager2/soundmanager2.swf +0 -0
- data/spec/javascripts/helpers/soundmanager2/soundmanager2_debug.swf +0 -0
- data/spec/javascripts/src/core/comp_spec.js +5 -6
- data/spec/javascripts/src/core/load_spec.js +2 -1
- data/spec/javascripts/src/core/re_spec.js +5 -6
- data/spec/javascripts/src/cycle/tick_spec.js +2 -2
- data/spec/javascripts/src/input/keyboard_spec.js +1 -5
- data/spec/javascripts/src/media/sound_spec.js +3 -3
- data/spec/lib/entityjs/assets_spec.rb +20 -0
- data/spec/lib/entityjs/commands/build_spec.rb +18 -38
- data/spec/lib/entityjs/commands/release_spec.rb +11 -0
- data/src/core/entity.js +18 -48
- data/src/core/load.js +71 -38
- data/src/core/re.js +3 -25
- data/src/core/system.js +1 -0
- data/src/display/align.js +3 -0
- data/src/input/keyboard.js +1 -2
- data/src/input/mouse.js +0 -1
- data/src/math/tile.js +2 -0
- data/src/media/sound.js +20 -20
- data/src/util/log.js +2 -10
- metadata +22 -15
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -7,12 +7,12 @@ An HTML5 javascript game engine utlizing the entity-component design. Write high
|
|
7
7
|
|
8
8
|
Version 0.3 is a major release with many revisions and a brand new ruby gem to work with. You can now type `entityjs new game-name` and a new game will be created. Of course there are more commands, check below.
|
9
9
|
|
10
|
-
There is a new directory structure, testing framework, better minifier, better config file and more.
|
10
|
+
There is a new directory structure, testing framework, better minifier, better config file and more.
|
11
11
|
|
12
|
-
**Warning:** there are many name changes which will break older
|
12
|
+
**Warning:** there are many name changes which will break older entity games!
|
13
13
|
|
14
14
|
## API
|
15
|
-
Currently the [API](http://entityjs.com/api) is out of date. It will be updated
|
15
|
+
Currently the [API](http://entityjs.com/api) is out of date. It will slowly be updated everyday.
|
16
16
|
|
17
17
|
## What makes this different from other javascript game engines?
|
18
18
|
Entity strives to be the most flexible game engine available. We understand no one likes rewritting the same functions and lines of code over and over. So we have developed a solution to this problem and that is the component-entity design. The traditional approach to game engine design is creating a hierarchy of classes. This is infact the **most** tightly coupled design. This creates close coupled classes whos functionality is strictly typed to one class, its not easily portable to other projects, good luck copying that one needed function and as game development progresses classes get bigger and more complex. You will eventually end up with *god-classes* who control most of the game logic.
|
@@ -36,8 +36,6 @@ This will install the latest version of the gem and now you can easily create so
|
|
36
36
|
|
37
37
|
## Usage
|
38
38
|
|
39
|
-
**Warning** at the moment the gemfile is at version 0.2.2 and is **broken**. Wait until version 0.3 to try these commands.
|
40
|
-
|
41
39
|
When using these commands make sure you are always in the root directory of your game.
|
42
40
|
|
43
41
|
### Creating a New Game
|
@@ -117,7 +115,7 @@ Assets are located here:
|
|
117
115
|
* `Inherit()` on entities is now `def()`
|
118
116
|
* `Extend()` on entities is now `attr()`
|
119
117
|
|
120
|
-
There are many more name changes, make sure to read the component source code for
|
118
|
+
There are many more name changes, make sure to read the component source code for help. Most components have a usage example to help you along while the API / tutorials get up to date.
|
121
119
|
|
122
120
|
### Short getters and setters
|
123
121
|
|
@@ -143,13 +141,52 @@ There are many more name changes, make sure to read the component source code fo
|
|
143
141
|
|
144
142
|
## QUnit Testing
|
145
143
|
|
146
|
-
All games
|
147
|
-
|
148
|
-
|
144
|
+
All games use [QUnit](http://docs.jquery.com/QUnit) for testing, its light weight and easy to use. Checkout the platform template to see some example tests.
|
145
|
+
|
146
|
+
### Factories
|
147
|
+
|
148
|
+
Factories are used to easily create complex entities. During tests you may need access to a specific type of entity multiple times. Factories make it easy to create any kind of entity at anytime.
|
149
|
+
|
150
|
+
Simply create a new `factories.js` in the `/tests` directory and add something like below.
|
151
|
+
|
152
|
+
factory('enemy', function(){
|
153
|
+
//make a custom coin
|
154
|
+
this.health = 100;
|
155
|
+
this.state = 'idle';
|
156
|
+
|
157
|
+
//can use normal entity methods
|
158
|
+
this.on('update', function(){
|
159
|
+
//something
|
160
|
+
});
|
161
|
+
});
|
162
|
+
|
163
|
+
//create new enemy entity anywhere in tests
|
164
|
+
var e = factory('enemy');
|
165
|
+
eq(e.state, 'idle') //true
|
166
|
+
|
167
|
+
//Same as...
|
168
|
+
var e = re.e('enemy');
|
169
|
+
e.health = 100;
|
170
|
+
//etc...
|
171
|
+
|
172
|
+
What if you need multiple enemy factories?
|
173
|
+
|
174
|
+
//use f for laziness
|
175
|
+
f('enemy attacking', function(){
|
176
|
+
this.state = 'attacking';
|
177
|
+
});
|
178
|
+
|
179
|
+
### EntityJS Helpers
|
180
|
+
|
181
|
+
Some asserts have been added for checking entities, like `expectTrigger`, `expectFlicker` and `expectListener`. For more info check `localhost:2345/qunit/qunit.entity.js`.
|
182
|
+
|
183
|
+
### Input Helpers
|
184
|
+
|
185
|
+
Special methods like `keypress()` and `click()` are available to simulate user input. Check `localhost:2345/qunit/qunit.input.js` for more information.
|
149
186
|
|
150
187
|
## Tile Map Editor
|
151
188
|
|
152
|
-
The awesome [tiled](http://www.mapeditor.org/)
|
189
|
+
The awesome [tiled map editor](http://www.mapeditor.org/) is now compatible and can be used in your projects.
|
153
190
|
|
154
191
|
Simply create a new directory in /assets named levels or anything you like to save your maps in. They can accessed in code like so:
|
155
192
|
|
@@ -161,10 +198,10 @@ If you are still confused create a new platform game and view how the levels are
|
|
161
198
|
## Quick Start Guide
|
162
199
|
First you should install [ruby](http://rubyinstaller.org/) and the [entityjs gem](http://rubygems.org/gems/entityjs).
|
163
200
|
|
164
|
-
Now you can create a new game:
|
165
|
-
`entityjs new mygame`
|
201
|
+
Now you can create a new game from the platform template:
|
202
|
+
`entityjs new mygame platform`
|
166
203
|
|
167
|
-
Move into the mygame
|
204
|
+
Move into the `mygame` directory and play the game:
|
168
205
|
`entityjs server`
|
169
206
|
|
170
207
|
Open your browser and navigate to `localhost:2345`
|
data/entityjs.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.email = ["ben@entityjs.com"]
|
10
10
|
s.homepage = "http://entityjs.com"
|
11
11
|
s.summary = %q{Create HTML5 javascript games in EntityJS.}
|
12
|
-
s.description = %q{
|
12
|
+
s.description = %q{HTML5 Javascript game engine, quickly create robust, flexible and reusable games.}
|
13
13
|
|
14
14
|
s.license = 'MIT'
|
15
15
|
|
data/lib/entityjs/assets.rb
CHANGED
@@ -7,6 +7,10 @@ module Entityjs
|
|
7
7
|
['xml', 'json', 'tmx']
|
8
8
|
end
|
9
9
|
|
10
|
+
def self.datas_regex
|
11
|
+
return /^.*\.#{self.valid_datas.join('|')}$/i
|
12
|
+
end
|
13
|
+
|
10
14
|
def self.set_vars(contents, tests=false)
|
11
15
|
#read file for changes
|
12
16
|
Config.instance.reload
|
@@ -46,26 +50,37 @@ module Entityjs
|
|
46
50
|
js
|
47
51
|
end
|
48
52
|
|
49
|
-
|
50
|
-
|
51
|
-
|
53
|
+
#converts all assets / info into a js game header
|
54
|
+
def self.to_js(path = nil, images = nil, sounds = nil, canvas = nil, datas = nil)
|
55
|
+
path ||= 'assets/'
|
56
|
+
images ||= self.images_to_js
|
57
|
+
sounds ||= self.sounds_to_js
|
58
|
+
canvas ||= Config.instance.canvas_id
|
59
|
+
datas ||= self.datas_to_js
|
60
|
+
|
61
|
+
return %Q(
|
62
|
+
re.load.path = '#{path}';
|
52
63
|
re.assets = {
|
53
|
-
images:#{
|
54
|
-
sounds:#{
|
64
|
+
images:#{images},
|
65
|
+
sounds:#{sounds}
|
55
66
|
};
|
56
|
-
re.canvas = '##{
|
57
|
-
#{
|
67
|
+
re.canvas = '##{canvas}';
|
68
|
+
#{datas}
|
58
69
|
)
|
59
70
|
end
|
60
71
|
|
61
|
-
def self.images_to_js
|
62
|
-
|
72
|
+
def self.images_to_js(images = nil)
|
73
|
+
images ||= self.search('images')
|
74
|
+
|
75
|
+
s = images.collect{|i| "'#{i}'"}.join(', ')
|
63
76
|
|
64
77
|
"[#{s}]"
|
65
78
|
end
|
66
79
|
|
67
|
-
def self.sounds_to_js
|
68
|
-
|
80
|
+
def self.sounds_to_js(sounds = nil)
|
81
|
+
sounds ||= self.search('sounds')
|
82
|
+
|
83
|
+
s = sounds.collect{|i| "'#{i}'"}.join(', ')
|
69
84
|
|
70
85
|
"[#{s}]"
|
71
86
|
end
|
@@ -77,58 +92,76 @@ module Entityjs
|
|
77
92
|
|
78
93
|
s.each do |i|
|
79
94
|
|
80
|
-
|
81
|
-
|
95
|
+
file = i
|
96
|
+
data = IO.read(Dirc.game_root+'/'+Config.assets_folder+'/'+i)
|
97
|
+
|
98
|
+
out += self.data_to_js(i, data)
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
out
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.data_to_js(file, data)
|
107
|
+
contents = self.data_to_json(file, data)
|
108
|
+
|
109
|
+
basename = File.basename(file)
|
110
|
+
dirname = File.dirname(file)
|
111
|
+
|
112
|
+
comps = []
|
113
|
+
|
114
|
+
dirname.split('/').each do |i|
|
82
115
|
|
83
116
|
#make singular
|
84
|
-
if
|
85
|
-
|
117
|
+
if i[-1] == 's'
|
118
|
+
i = i[0..-2]
|
86
119
|
end
|
87
120
|
|
88
|
-
|
89
|
-
|
90
|
-
out += %Q(
|
91
|
-
re.e('#{basename} #{dirname}')
|
92
|
-
.attr(#{contents});
|
93
|
-
)
|
121
|
+
comps.push i
|
94
122
|
|
95
123
|
end
|
96
124
|
|
125
|
+
#remove dot because no base dir was given
|
126
|
+
comps.delete_if{|i| i=='.'}
|
97
127
|
|
98
|
-
|
128
|
+
%Q(
|
129
|
+
re.e('#{basename} #{comps.join(' ')}')
|
130
|
+
.attr(#{contents});
|
131
|
+
)
|
99
132
|
end
|
100
133
|
|
101
|
-
def self.data_to_json(
|
134
|
+
def self.data_to_json(file, data)
|
135
|
+
ext = file.downcase
|
136
|
+
|
102
137
|
case ext
|
103
|
-
when /json
|
138
|
+
when /json$/
|
104
139
|
return data
|
105
140
|
|
106
|
-
when /tmx
|
141
|
+
when /tmx$/
|
107
142
|
|
108
143
|
return ParseTMX.parse(data)
|
109
144
|
|
110
|
-
when /xml
|
145
|
+
when /xml$/
|
111
146
|
return ParseXML.parse(data)
|
112
147
|
|
113
|
-
when /csv
|
148
|
+
when /csv$/
|
114
149
|
raise 'CSV files are not supported at the moment'
|
115
150
|
|
116
|
-
when /yml
|
151
|
+
when /yml$/
|
117
152
|
raise 'YML files are not supported at the moment'
|
118
153
|
|
119
154
|
else
|
120
|
-
|
155
|
+
return data
|
121
156
|
|
122
157
|
end
|
123
|
-
|
124
|
-
return nil
|
158
|
+
|
125
159
|
end
|
126
160
|
|
127
161
|
def self.file_to_json(file)
|
128
162
|
contents = IO.read(file)
|
129
|
-
|
130
|
-
|
131
|
-
return self.data_to_json(contents, ext)
|
163
|
+
|
164
|
+
return self.data_to_json(file, contents)
|
132
165
|
end
|
133
166
|
|
134
167
|
def self.search(type='*')
|
@@ -151,8 +184,7 @@ module Entityjs
|
|
151
184
|
end
|
152
185
|
|
153
186
|
def self.search_datas
|
154
|
-
|
155
|
-
return self.find_files("#{Config.assets_folder}/*/*").select{|i| !i.match(/\/(images|sounds)\//i) && i.match(/^*\.(#{datas})$/i)}
|
187
|
+
return self.find_files("#{Config.assets_folder}/*/*").select{|i| !i.match(/\/(images|sounds)\//i) && i.match(self.datas_regex)}
|
156
188
|
end
|
157
189
|
|
158
190
|
def self.find_files(search)
|
data/lib/entityjs/command.rb
CHANGED
@@ -14,8 +14,6 @@ module Entityjs
|
|
14
14
|
|
15
15
|
Config.instance.reload
|
16
16
|
|
17
|
-
license = Config.instance.license
|
18
|
-
|
19
17
|
if name.nil? || name.empty?
|
20
18
|
date = Time.now.strftime('%s')
|
21
19
|
name = "build-#{date}"
|
@@ -29,6 +27,9 @@ module Entityjs
|
|
29
27
|
sounds_folder = Config.sounds_folder
|
30
28
|
scripts_folder = Config.scripts_folder
|
31
29
|
|
30
|
+
final_name = 'game.min.js'
|
31
|
+
html_name = 'play.html'
|
32
|
+
|
32
33
|
#build if it doesn't exist
|
33
34
|
Dirc.create_dir('builds', true)
|
34
35
|
|
@@ -60,51 +61,32 @@ module Entityjs
|
|
60
61
|
|
61
62
|
#append all files into one big file
|
62
63
|
puts "Compiling code"
|
63
|
-
out = ''
|
64
|
-
|
65
|
-
scripts = Dirc.find_scripts(Config.instance.scripts_ignore, Config.instance.scripts_order)
|
66
|
-
entities = Dirc.find_entity_src(Config.instance.entity_ignore)
|
67
64
|
|
68
|
-
|
69
|
-
|
70
|
-
out += IO.read(i)
|
71
|
-
out += "\n"
|
72
|
-
end
|
73
|
-
|
74
|
-
#add version
|
75
|
-
out = out.gsub(/\$VERSION/, Entityjs::VERSION)
|
76
|
-
|
77
|
-
scripts.each do |i|
|
78
|
-
out += "\n"
|
79
|
-
out += IO.read(i)
|
80
|
-
out += "\n"
|
81
|
-
end
|
82
|
-
|
83
|
-
#add levels, animations etc data
|
84
|
-
out += Assets.to_js
|
65
|
+
entity_src = self.compile_entity(Config.instance.entity_ignore)
|
66
|
+
scripts = self.compile_scripts(Config.instance.scripts_ignore, Config.instance.scripts_order)
|
85
67
|
|
68
|
+
out = entity_src+scripts
|
86
69
|
|
87
70
|
#minify
|
88
71
|
puts "Almost done..."
|
89
72
|
|
90
73
|
#save
|
91
|
-
File.open(
|
92
|
-
f.write(license)
|
74
|
+
File.open(final_name, 'w') do |f|
|
93
75
|
|
94
|
-
f.write(
|
76
|
+
f.write(self.minify(out))
|
95
77
|
|
96
78
|
f.close
|
97
79
|
end
|
98
80
|
|
99
|
-
|
100
81
|
#create play.html
|
101
82
|
puts "Creating play page"
|
102
83
|
|
103
|
-
File.open(
|
84
|
+
File.open(html_name, 'w') do |f|
|
104
85
|
f.write(%Q(<!DOCTYPE html>
|
105
86
|
<html>
|
106
87
|
<head>
|
107
|
-
<
|
88
|
+
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
|
89
|
+
<script src='#{final_name}' type='text/javascript'></script>
|
108
90
|
</head>
|
109
91
|
<body>
|
110
92
|
<canvas id='#{Config.instance.canvas_id}' width='#{Config.instance.width}' height='#{Config.instance.height}'>Error browser does not support canvas element.</canvas>
|
@@ -123,6 +105,53 @@ module Entityjs
|
|
123
105
|
return 0
|
124
106
|
end
|
125
107
|
|
108
|
+
#compiles all entity source and returns it
|
109
|
+
def self.compile_entity(ignore = nil)
|
110
|
+
out = ''
|
111
|
+
entities = Dirc.find_entity_src(ignore)
|
112
|
+
entities.each do |i|
|
113
|
+
out += "\n"
|
114
|
+
out += IO.read(i)
|
115
|
+
out += "\n"
|
116
|
+
end
|
117
|
+
|
118
|
+
#add version
|
119
|
+
out = out.gsub(/\$VERSION/, Entityjs::VERSION)
|
120
|
+
|
121
|
+
return out
|
122
|
+
end
|
123
|
+
|
124
|
+
#compiles all game source and returns it
|
125
|
+
def self.compile_scripts(ignore = nil, order=nil)
|
126
|
+
scripts = Dirc.find_scripts(ignore, order)
|
127
|
+
|
128
|
+
out = ''
|
129
|
+
|
130
|
+
scripts.each do |i|
|
131
|
+
out += "\n"
|
132
|
+
out += IO.read(i)
|
133
|
+
out += "\n"
|
134
|
+
end
|
135
|
+
|
136
|
+
#add levels, animations etc data
|
137
|
+
out += Assets.to_js
|
138
|
+
|
139
|
+
return out
|
140
|
+
end
|
141
|
+
|
142
|
+
#minifies source and returns it
|
143
|
+
def self.minify(code, license=true)
|
144
|
+
|
145
|
+
code = Uglifier.compile(code, :copyright=>false)
|
146
|
+
|
147
|
+
#add entity license statement
|
148
|
+
if license
|
149
|
+
code = Config.instance.license + code
|
150
|
+
end
|
151
|
+
|
152
|
+
return code
|
153
|
+
end
|
154
|
+
|
126
155
|
end
|
127
156
|
|
128
157
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Entityjs
|
2
|
+
|
3
|
+
#compiles entity source code
|
4
|
+
class Release
|
5
|
+
|
6
|
+
def self.generate(name=nil)
|
7
|
+
|
8
|
+
if name.is_a? Array
|
9
|
+
name = name.first
|
10
|
+
end
|
11
|
+
|
12
|
+
name ||= self.release_name
|
13
|
+
|
14
|
+
puts "Collecting files"
|
15
|
+
|
16
|
+
min = Entityjs::Build.compile_entity
|
17
|
+
|
18
|
+
puts "Minifying"
|
19
|
+
|
20
|
+
min = Entityjs::Build.minify(min)
|
21
|
+
|
22
|
+
File.open(name, 'w') do |f|
|
23
|
+
|
24
|
+
f.write(min)
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
puts "Done!"
|
29
|
+
puts "File is at"
|
30
|
+
puts " ./#{name}"
|
31
|
+
|
32
|
+
|
33
|
+
return 0
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.release_name
|
37
|
+
return "entity-#{Entityjs::VERSION}.min.js"
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -3,6 +3,9 @@ module Entityjs
|
|
3
3
|
class ParseTMX
|
4
4
|
|
5
5
|
def self.parse(data)
|
6
|
+
if data.nil? || data.empty?
|
7
|
+
return '{}'
|
8
|
+
end
|
6
9
|
|
7
10
|
contents = ParseXML.parse_to_hash(data)
|
8
11
|
|
@@ -11,21 +14,18 @@ module Entityjs
|
|
11
14
|
contents['layer'].each do |k|
|
12
15
|
self.parse_layer(k)
|
13
16
|
end
|
14
|
-
|
17
|
+
elsif contents['layer'].is_a? Hash
|
15
18
|
self.parse_layer(contents['layer'])
|
16
19
|
end
|
17
20
|
|
18
|
-
#transform into
|
19
|
-
|
20
|
-
|
21
|
-
#transform string-numbers into numbers
|
22
|
-
|
23
|
-
out = out.gsub(/"[0-9\.]*"/){|s| s[1..-2] }
|
24
|
-
|
25
|
-
return out
|
21
|
+
#transform into strin
|
22
|
+
return ParseXML.parse(contents)
|
26
23
|
end
|
27
24
|
|
28
25
|
def self.parse_layer(k)
|
26
|
+
if k.nil? || k.empty?
|
27
|
+
return
|
28
|
+
end
|
29
29
|
|
30
30
|
map = k['data']
|
31
31
|
#remove encoding
|
@@ -3,6 +3,9 @@ module Entityjs
|
|
3
3
|
class ParseXML
|
4
4
|
|
5
5
|
def self.parse_to_hash(contents)
|
6
|
+
if contents.nil? || contents.empty?
|
7
|
+
return '{}'
|
8
|
+
end
|
6
9
|
#might need different transfomration
|
7
10
|
#remove header
|
8
11
|
|
@@ -19,10 +22,13 @@ module Entityjs
|
|
19
22
|
end
|
20
23
|
|
21
24
|
return contents
|
22
|
-
|
23
25
|
end
|
24
26
|
|
25
27
|
def self.parse(contents)
|
28
|
+
if contents.nil? || contents.empty?
|
29
|
+
return '{}'
|
30
|
+
end
|
31
|
+
|
26
32
|
if contents.is_a? String
|
27
33
|
contents = self.parse_to_hash(contents)
|
28
34
|
end
|
@@ -31,7 +37,12 @@ module Entityjs
|
|
31
37
|
contents = contents.to_json
|
32
38
|
|
33
39
|
#remove @
|
34
|
-
|
40
|
+
contents = contents.gsub('"@','"')
|
41
|
+
|
42
|
+
|
43
|
+
#transform string-numbers into numbers
|
44
|
+
|
45
|
+
return contents.gsub(/"[0-9\.]*"/){|s| s[1..-2] }
|
35
46
|
end
|
36
47
|
|
37
48
|
end
|
data/lib/entityjs/version.rb
CHANGED
data/lib/entityjs.rb
CHANGED
data/public/play.html
CHANGED
data/public/tests.html
CHANGED
@@ -39,6 +39,13 @@ function match(test, reg, i){
|
|
39
39
|
|
40
40
|
}
|
41
41
|
|
42
|
+
var indexOf = function(z, a,f){for(var c=z.length,r=-1,d=f>>>0;~(c-d);r=z[--c]===a?c:r);return r};
|
43
|
+
|
44
|
+
function contains(array, item){
|
45
|
+
ok(indexOf(array, item) != -1);
|
46
|
+
}
|
47
|
+
|
48
|
+
|
42
49
|
function called(value){
|
43
50
|
if(arguments.length == 1){
|
44
51
|
expect(value).toHaveBeenCalled()
|
@@ -0,0 +1,28 @@
|
|
1
|
+
/*
|
2
|
+
* FlashCanvas
|
3
|
+
*
|
4
|
+
* Copyright (c) 2009 Tim Cameron Ryan
|
5
|
+
* Copyright (c) 2009-2011 FlashCanvas Project
|
6
|
+
* Released under the MIT/X License
|
7
|
+
*/
|
8
|
+
window.ActiveXObject&&!window.CanvasRenderingContext2D&&function(h,j){function D(a){this.code=a;this.message=T[a]}function U(a){this.width=a}function E(a){this.id=a.C++}function t(a){this.G=a;this.id=a.C++}function u(a,b){this.canvas=a;this.B=b;this.d=a.uniqueID;this.D();this.C=0;this.t="";var c=this;setInterval(function(){n[c.d]===0&&c.e()},30)}function A(){if(j.readyState==="complete"){j.detachEvent(F,A);for(var a=j.getElementsByTagName(r),b=0,c=a.length;b<c;++b)B.initElement(a[b])}}function G(){var a=
|
9
|
+
event.srcElement,b=a.parentNode;a.blur();b.focus()}function H(){var a=event.propertyName;if(a==="width"||a==="height"){var b=event.srcElement,c=b[a],d=parseInt(c,10);if(isNaN(d)||d<0)d=a==="width"?300:150;if(c===d){b.style[a]=d+"px";b.getContext("2d").I(b.width,b.height)}else b[a]=d}}function I(){h.detachEvent(J,I);for(var a in s){var b=s[a],c=b.firstChild,d;for(d in c)if(typeof c[d]==="function")c[d]=k;for(d in b)if(typeof b[d]==="function")b[d]=k;c.detachEvent(K,G);b.detachEvent(L,H)}h[M]=k;h[N]=
|
10
|
+
k;h[O]=k;h[C]=k;h[P]=k}function V(){var a=j.getElementsByTagName("script");a=a[a.length-1];return j.documentMode>=8?a.src:a.getAttribute("src",4)}function v(a){return(""+a).replace(/&/g,"&").replace(/</g,"<")}function W(a){return a.toLowerCase()}function i(a){throw new D(a);}function Q(a){var b=parseInt(a.width,10),c=parseInt(a.height,10);if(isNaN(b)||b<0)b=300;if(isNaN(c)||c<0)c=150;a.width=b;a.height=c}var k=null,r="canvas",M="CanvasRenderingContext2D",N="CanvasGradient",O="CanvasPattern",
|
11
|
+
C="FlashCanvas",P="G_vmlCanvasManager",K="onfocus",L="onpropertychange",F="onreadystatechange",J="onunload",w=((h[C+"Options"]||{}).swfPath||V().replace(/[^\/]+$/,""))+"flashcanvas.swf",e=new function(a){for(var b=0,c=a.length;b<c;b++)this[a[b]]=b}(["toDataURL","save","restore","scale","rotate","translate","transform","setTransform","globalAlpha","globalCompositeOperation","strokeStyle","fillStyle","createLinearGradient","createRadialGradient","createPattern","lineWidth","lineCap","lineJoin","miterLimit",
|
12
|
+
"shadowOffsetX","shadowOffsetY","shadowBlur","shadowColor","clearRect","fillRect","strokeRect","beginPath","closePath","moveTo","lineTo","quadraticCurveTo","bezierCurveTo","arcTo","rect","arc","fill","stroke","clip","isPointInPath","font","textAlign","textBaseline","fillText","strokeText","measureText","drawImage","createImageData","getImageData","putImageData","addColorStop","direction","resize"]),x={},n={},s={},y={};u.prototype={save:function(){this.b();this.c();this.m();this.l();this.z();this.w();
|
13
|
+
this.F.push([this.f,this.g,this.A,this.u,this.j,this.h,this.i,this.k,this.p,this.q,this.n,this.o,this.v,this.r,this.s]);this.a.push(e.save)},restore:function(){var a=this.F;if(a.length){a=a.pop();this.globalAlpha=a[0];this.globalCompositeOperation=a[1];this.strokeStyle=a[2];this.fillStyle=a[3];this.lineWidth=a[4];this.lineCap=a[5];this.lineJoin=a[6];this.miterLimit=a[7];this.shadowOffsetX=a[8];this.shadowOffsetY=a[9];this.shadowBlur=a[10];this.shadowColor=a[11];this.font=a[12];this.textAlign=a[13];
|
14
|
+
this.textBaseline=a[14]}this.a.push(e.restore)},scale:function(a,b){this.a.push(e.scale,a,b)},rotate:function(a){this.a.push(e.rotate,a)},translate:function(a,b){this.a.push(e.translate,a,b)},transform:function(a,b,c,d,f,g){this.a.push(e.transform,a,b,c,d,f,g)},setTransform:function(a,b,c,d,f,g){this.a.push(e.setTransform,a,b,c,d,f,g)},b:function(){var a=this.a;if(this.f!==this.globalAlpha){this.f=this.globalAlpha;a.push(e.globalAlpha,this.f)}if(this.g!==this.globalCompositeOperation){this.g=this.globalCompositeOperation;
|
15
|
+
a.push(e.globalCompositeOperation,this.g)}},m:function(){if(this.A!==this.strokeStyle){var a=this.A=this.strokeStyle;this.a.push(e.strokeStyle,typeof a==="object"?a.id:a)}},l:function(){if(this.u!==this.fillStyle){var a=this.u=this.fillStyle;this.a.push(e.fillStyle,typeof a==="object"?a.id:a)}},createLinearGradient:function(a,b,c,d){isFinite(a)&&isFinite(b)&&isFinite(c)&&isFinite(d)||i(9);this.a.push(e.createLinearGradient,a,b,c,d);return new t(this)},createRadialGradient:function(a,b,c,d,f,g){isFinite(a)&&
|
16
|
+
isFinite(b)&&isFinite(c)&&isFinite(d)&&isFinite(f)&&isFinite(g)||i(9);if(c<0||g<0)i(1);this.a.push(e.createRadialGradient,a,b,c,d,f,g);return new t(this)},createPattern:function(a,b){a||i(17);var c=a.tagName,d,f=this.d;if(c){c=c.toLowerCase();if(c==="img")d=a.getAttribute("src",2);else if(c===r||c==="video")return;else i(17)}else if(a.src)d=a.src;else i(17);b==="repeat"||b==="no-repeat"||b==="repeat-x"||b==="repeat-y"||b===""||b===k||i(12);this.a.push(e.createPattern,v(d),b);if(x[f]){this.e();++n[f]}return new E(this)},
|
17
|
+
z:function(){var a=this.a;if(this.j!==this.lineWidth){this.j=this.lineWidth;a.push(e.lineWidth,this.j)}if(this.h!==this.lineCap){this.h=this.lineCap;a.push(e.lineCap,this.h)}if(this.i!==this.lineJoin){this.i=this.lineJoin;a.push(e.lineJoin,this.i)}if(this.k!==this.miterLimit){this.k=this.miterLimit;a.push(e.miterLimit,this.k)}},c:function(){var a=this.a;if(this.p!==this.shadowOffsetX){this.p=this.shadowOffsetX;a.push(e.shadowOffsetX,this.p)}if(this.q!==this.shadowOffsetY){this.q=this.shadowOffsetY;
|
18
|
+
a.push(e.shadowOffsetY,this.q)}if(this.n!==this.shadowBlur){this.n=this.shadowBlur;a.push(e.shadowBlur,this.n)}if(this.o!==this.shadowColor){this.o=this.shadowColor;a.push(e.shadowColor,this.o)}},clearRect:function(a,b,c,d){this.a.push(e.clearRect,a,b,c,d)},fillRect:function(a,b,c,d){this.b();this.c();this.l();this.a.push(e.fillRect,a,b,c,d)},strokeRect:function(a,b,c,d){this.b();this.c();this.m();this.z();this.a.push(e.strokeRect,a,b,c,d)},beginPath:function(){this.a.push(e.beginPath)},closePath:function(){this.a.push(e.closePath)},
|
19
|
+
moveTo:function(a,b){this.a.push(e.moveTo,a,b)},lineTo:function(a,b){this.a.push(e.lineTo,a,b)},quadraticCurveTo:function(a,b,c,d){this.a.push(e.quadraticCurveTo,a,b,c,d)},bezierCurveTo:function(a,b,c,d,f,g){this.a.push(e.bezierCurveTo,a,b,c,d,f,g)},arcTo:function(a,b,c,d,f){f<0&&isFinite(f)&&i(1);this.a.push(e.arcTo,a,b,c,d,f)},rect:function(a,b,c,d){this.a.push(e.rect,a,b,c,d)},arc:function(a,b,c,d,f,g){c<0&&isFinite(c)&&i(1);this.a.push(e.arc,a,b,c,d,f,g?1:0)},fill:function(){this.b();this.c();
|
20
|
+
this.l();this.a.push(e.fill)},stroke:function(){this.b();this.c();this.m();this.z();this.a.push(e.stroke)},clip:function(){this.a.push(e.clip)},w:function(){var a=this.a;if(this.v!==this.font)try{var b=y[this.d];b.style.font=this.v=this.font;var c=b.currentStyle;a.push(e.font,[c.fontStyle,c.fontWeight,b.offsetHeight,c.fontFamily].join(" "))}catch(d){}if(this.r!==this.textAlign){this.r=this.textAlign;a.push(e.textAlign,this.r)}if(this.s!==this.textBaseline){this.s=this.textBaseline;a.push(e.textBaseline,
|
21
|
+
this.s)}if(this.t!==this.canvas.currentStyle.direction){this.t=this.canvas.currentStyle.direction;a.push(e.direction,this.t)}},fillText:function(a,b,c,d){this.b();this.l();this.c();this.w();this.a.push(e.fillText,v(a),b,c,d===void 0?Infinity:d)},strokeText:function(a,b,c,d){this.b();this.m();this.c();this.w();this.a.push(e.strokeText,v(a),b,c,d===void 0?Infinity:d)},measureText:function(a){var b=y[this.d];try{b.style.font=this.font}catch(c){}b.innerText=a.replace(/[ \n\f\r]/g,"\t");return new U(b.offsetWidth)},
|
22
|
+
drawImage:function(a,b,c,d,f,g,o,l,z){a||i(17);var p=a.tagName,m,q=arguments.length,R=this.d;if(p){p=p.toLowerCase();if(p==="img")m=a.getAttribute("src",2);else if(p===r||p==="video")return;else i(17)}else if(a.src)m=a.src;else i(17);this.b();this.c();m=v(m);if(q===3)this.a.push(e.drawImage,q,m,b,c);else if(q===5)this.a.push(e.drawImage,q,m,b,c,d,f);else if(q===9){if(d===0||f===0)i(1);this.a.push(e.drawImage,q,m,b,c,d,f,g,o,l,z)}else return;if(x[R]){this.e();++n[R]}},D:function(){this.globalAlpha=
|
23
|
+
this.f=1;this.globalCompositeOperation=this.g="source-over";this.fillStyle=this.u=this.strokeStyle=this.A="#000000";this.lineWidth=this.j=1;this.lineCap=this.h="butt";this.lineJoin=this.i="miter";this.miterLimit=this.k=10;this.shadowBlur=this.n=this.shadowOffsetY=this.q=this.shadowOffsetX=this.p=0;this.shadowColor=this.o="rgba(0, 0, 0, 0.0)";this.font=this.v="10px sans-serif";this.textAlign=this.r="start";this.textBaseline=this.s="alphabetic";this.a=[];this.F=[]},H:function(){var a=this.a;this.a=
|
24
|
+
[];return a},e:function(){var a=this.H();if(a.length>0)return eval(this.B.CallFunction('<invoke name="executeCommand" returntype="javascript"><arguments><string>'+a.join("�")+"</string></arguments></invoke>"))},I:function(a,b){this.e();this.D();if(a>0)this.B.width=a;if(b>0)this.B.height=b;this.a.push(e.resize,a,b)}};t.prototype={addColorStop:function(a,b){if(isNaN(a)||a<0||a>1)i(1);this.G.a.push(e.addColorStop,this.id,a,b)}};D.prototype=Error();var T={1:"INDEX_SIZE_ERR",9:"NOT_SUPPORTED_ERR",11:"INVALID_STATE_ERR",
|
25
|
+
12:"SYNTAX_ERR",17:"TYPE_MISMATCH_ERR",18:"SECURITY_ERR"},B={initElement:function(a){if(a.getContext)return a;var b=a.uniqueID,c="external"+b;x[b]=false;n[b]=1;Q(a);a.innerHTML='<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="'+location.protocol+'//fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" width="100%" height="100%" id="'+c+'"><param name="allowScriptAccess" value="always"><param name="flashvars" value="id='+c+'"><param name="wmode" value="transparent"></object><span style="margin:0;padding:0;border:0;display:inline-block;position:static;height:1em;overflow:visible;white-space:nowrap"></span>';
|
26
|
+
s[b]=a;var d=a.firstChild;y[b]=a.lastChild;var f=j.body.contains;if(f(a))d.movie=w;else var g=setInterval(function(){if(f(a)){clearInterval(g);d.movie=w}},0);if(j.compatMode==="BackCompat"||!h.XMLHttpRequest)y[b].style.overflow="hidden";var o=new u(a,d);a.getContext=function(l){return l==="2d"?o:k};a.toDataURL=function(l,z){(""+l).replace(/[A-Z]+/g,W)==="image/jpeg"?o.a.push(e.toDataURL,l,typeof z==="number"?z:""):o.a.push(e.toDataURL,l);return o.e()};d.attachEvent(K,G);return a},saveImage:function(a){a.firstChild.saveImage()},
|
27
|
+
setOptions:function(){},trigger:function(a,b){s[a].fireEvent("on"+b)},unlock:function(a,b){n[a]&&--n[a];if(b){var c=s[a],d=c.firstChild,f,g;Q(c);f=c.width;g=c.height;c.style.width=f+"px";c.style.height=g+"px";if(f>0)d.width=f;if(g>0)d.height=g;d.resize(f,g);c.attachEvent(L,H);x[a]=true}}};j.createElement(r);j.createStyleSheet().cssText=r+"{display:inline-block;overflow:hidden;width:300px;height:150px}";j.readyState==="complete"?A():j.attachEvent(F,A);h.attachEvent(J,I);if(w.indexOf(location.protocol+
|
28
|
+
"//"+location.host+"/")===0){var S=new ActiveXObject("Microsoft.XMLHTTP");S.open("GET",w,false);S.send(k)}h[M]=u;h[N]=t;h[O]=E;h[C]=B;h[P]={init:function(){},init_:function(){},initElement:B.initElement};keep=u.measureText}(window,document);
|