entityjs 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/.gitignore +2 -1
  2. data/README.md +50 -13
  3. data/entityjs.gemspec +1 -1
  4. data/lib/entityjs/assets.rb +68 -36
  5. data/lib/entityjs/command.rb +3 -0
  6. data/lib/entityjs/commands/build.rb +58 -29
  7. data/lib/entityjs/commands/release.rb +42 -0
  8. data/lib/entityjs/parsers/parse_tmx.rb +9 -9
  9. data/lib/entityjs/parsers/parse_xml.rb +13 -2
  10. data/lib/entityjs/version.rb +1 -1
  11. data/lib/entityjs.rb +1 -1
  12. data/public/play.html +1 -1
  13. data/public/tests.html +1 -0
  14. data/spec/javascripts/helpers/canvas.js +3 -0
  15. data/spec/javascripts/helpers/entityunit.js +7 -0
  16. data/spec/javascripts/helpers/flashcanvas.js~ +28 -0
  17. data/spec/javascripts/helpers/soundmanager2/soundmanager2.js +5021 -0
  18. data/spec/javascripts/helpers/soundmanager2/soundmanager2.js~ +5021 -0
  19. data/spec/javascripts/helpers/soundmanager2/soundmanager2.swf +0 -0
  20. data/spec/javascripts/helpers/soundmanager2/soundmanager2_debug.swf +0 -0
  21. data/spec/javascripts/src/core/comp_spec.js +5 -6
  22. data/spec/javascripts/src/core/load_spec.js +2 -1
  23. data/spec/javascripts/src/core/re_spec.js +5 -6
  24. data/spec/javascripts/src/cycle/tick_spec.js +2 -2
  25. data/spec/javascripts/src/input/keyboard_spec.js +1 -5
  26. data/spec/javascripts/src/media/sound_spec.js +3 -3
  27. data/spec/lib/entityjs/assets_spec.rb +20 -0
  28. data/spec/lib/entityjs/commands/build_spec.rb +18 -38
  29. data/spec/lib/entityjs/commands/release_spec.rb +11 -0
  30. data/src/core/entity.js +18 -48
  31. data/src/core/load.js +71 -38
  32. data/src/core/re.js +3 -25
  33. data/src/core/system.js +1 -0
  34. data/src/display/align.js +3 -0
  35. data/src/input/keyboard.js +1 -2
  36. data/src/input/mouse.js +0 -1
  37. data/src/math/tile.js +2 -0
  38. data/src/media/sound.js +20 -20
  39. data/src/util/log.js +2 -10
  40. metadata +22 -15
data/.gitignore CHANGED
@@ -5,4 +5,5 @@ Gemfile.lock
5
5
  pkg/*
6
6
  .redcar
7
7
  ._redcar
8
- mygame
8
+ mygame
9
+ *.min.js
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. This is currently in alpha and most of it doesn't work!
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 versions!
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 once version 0.3 is released.
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 more information. Also every component has a usage example.
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 will now use [QUnit](http://docs.jquery.com/QUnit) as the primary test framework. Its light weight and awesome.
147
-
148
- Special methods like `keypress` and `click` are available to simulate user input. Check `localhost:2345/qunit/qunit.input.js` for more information.
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/) map editor is now compatible and can be used in your projects.
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/ directory and lets play the game:
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{The EntityJS gem supplies easy to use commands for creating javascript games.}
12
+ s.description = %q{HTML5 Javascript game engine, quickly create robust, flexible and reusable games.}
13
13
 
14
14
  s.license = 'MIT'
15
15
 
@@ -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
- def self.to_js
50
- out = %Q(
51
- re.load.path = 'assets/';
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:#{self.images_to_js},
54
- sounds:#{self.sounds_to_js}
64
+ images:#{images},
65
+ sounds:#{sounds}
55
66
  };
56
- re.canvas = '##{Config.instance.canvas_id}';
57
- #{self.datas_to_js}
67
+ re.canvas = '##{canvas}';
68
+ #{datas}
58
69
  )
59
70
  end
60
71
 
61
- def self.images_to_js
62
- s = self.search('images').collect{|i| "'#{i}'"}.join(', ')
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
- s = self.search('sounds').collect{|i| "'#{i}'"}.join(', ')
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
- basename = File.basename(i)
81
- dirname = File.dirname(i)
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 dirname[-1] == 's'
85
- dirname = dirname[0..-2]
117
+ if i[-1] == 's'
118
+ i = i[0..-2]
86
119
  end
87
120
 
88
- contents = self.file_to_json(Dirc.game_root+'/'+Config.assets_folder+'/'+i)
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
- out
128
+ %Q(
129
+ re.e('#{basename} #{comps.join(' ')}')
130
+ .attr(#{contents});
131
+ )
99
132
  end
100
133
 
101
- def self.data_to_json(data, ext)
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
- raise "Extension #{ext} not recognized"
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
- #remove dot from extension
130
- ext = File.extname(file).downcase
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
- datas = self.valid_datas.join('|')
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)
@@ -23,6 +23,9 @@ module Entityjs
23
23
  when /^(test|t)$/
24
24
  return Entityjs::Test.generate(args)
25
25
 
26
+ when 'release'
27
+ return Entityjs::Release.generate(args)
28
+
26
29
  when /^(comp|c)$/
27
30
  return Entityjs::Comp.generate(args)
28
31
 
@@ -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
- entities.each do |i|
69
- out += "\n"
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('game.min.js', 'w') do |f|
92
- f.write(license)
74
+ File.open(final_name, 'w') do |f|
93
75
 
94
- f.write(Uglifier.compile(out, :copyright=>false))
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('play.html', 'w') do |f|
84
+ File.open(html_name, 'w') do |f|
104
85
  f.write(%Q(<!DOCTYPE html>
105
86
  <html>
106
87
  <head>
107
- <script src='game.min.js' type='text/javascript'></script>
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
- else
17
+ elsif contents['layer'].is_a? Hash
15
18
  self.parse_layer(contents['layer'])
16
19
  end
17
20
 
18
- #transform into string
19
- out = ParseXML.parse(contents)
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
- return contents.gsub('"@','"')
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
@@ -1,3 +1,3 @@
1
1
  module Entityjs
2
- VERSION = "0.3.0"
2
+ VERSION = "0.3.1"
3
3
  end
data/lib/entityjs.rb CHANGED
@@ -19,7 +19,7 @@ begin
19
19
  rescue LoadError
20
20
  puts "Could not load 'uglifier'"
21
21
  puts "run 'gem install uglifier'"
22
- exit
22
+ exit
23
23
  end
24
24
 
25
25
  begin
data/public/play.html CHANGED
@@ -2,7 +2,7 @@
2
2
  <html>
3
3
 
4
4
  <head>
5
- <meta http-equiv="X-UA-Compatible" content="chrome=1">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
6
6
 
7
7
  <title>EntityJS Play Game</title>
8
8
 
data/public/tests.html CHANGED
@@ -3,6 +3,7 @@
3
3
  <head>
4
4
  <title>EntityJS Test Suite</title>
5
5
 
6
+ <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
6
7
  <link rel="stylesheet" href="qunit/qunit.css" type="text/css" media="screen">
7
8
  <style type="text/css">
8
9
  canvas {
@@ -8,6 +8,9 @@ function teardown_canvas(){
8
8
  }
9
9
 
10
10
  $(function(){
11
+ //disable soundmanager
12
+ soundManager = null;
13
+
11
14
  setup_canvas();
12
15
 
13
16
  re.sys.init('#game-canvas');
@@ -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,"&amp;").replace(/</g,"&lt;")}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("&#0;")+"</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);