summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS4
-rw-r--r--ChangeLog23
-rw-r--r--README.md2
-rwxr-xr-xemcc39
-rw-r--r--emscripten-version.txt2
-rw-r--r--media/powered_by_logo.pngbin0 -> 24795 bytes
-rw-r--r--media/powered_by_logo.svg1547
-rw-r--r--media/switch_logo.epsbin1114650 -> 0 bytes
-rw-r--r--media/switch_logo.jpegbin35951 -> 0 bytes
-rw-r--r--media/switch_logo.pngbin0 -> 19222 bytes
-rwxr-xr-xscons-tools/emscripten.py58
-rw-r--r--src/closure-externs.js60
-rw-r--r--src/library.js26
-rw-r--r--src/library_browser.js55
-rw-r--r--src/library_egl.js6
-rw-r--r--src/library_glfw.js2
-rw-r--r--src/library_html5.js61
-rw-r--r--src/library_openal.js27
-rw-r--r--src/library_sdl.js13
-rw-r--r--src/library_sockfs.js38
-rw-r--r--src/postamble.js8
-rw-r--r--src/preamble.js17
-rw-r--r--src/relooper/Relooper.cpp53
-rw-r--r--src/relooper/Relooper.h8
-rw-r--r--src/relooper/test.cpp123
-rw-r--r--src/relooper/test.txt65
-rw-r--r--src/runtime.js5
-rw-r--r--src/settings.js20
-rw-r--r--src/shell.js4
-rw-r--r--system/include/emscripten/emscripten.h42
-rw-r--r--system/include/emscripten/html5.h18
-rw-r--r--tests/cases/aliases_fastcomp.ll41
-rw-r--r--tests/cases/aliases_fastcomp.txt5
-rw-r--r--tests/emscripten_log/emscripten_log.cpp11
-rwxr-xr-xtests/fuzz/csmith_driver.py4
-rw-r--r--tests/mem_init.cpp24
-rw-r--r--tests/openal_playback.cpp2
-rwxr-xr-xtests/runner.py5
-rw-r--r--tests/sdl_wm_togglefullscreen.c78
-rw-r--r--tests/sockets/p2p/.gitignore2
-rw-r--r--tests/sockets/p2p/broker/p2p-broker.js231
-rw-r--r--tests/sockets/p2p/client/p2p-client.js4485
-rw-r--r--tests/sockets/p2p/package.json17
-rw-r--r--tests/sockets/webrtc_host.c11
-rw-r--r--tests/sockets/webrtc_peer.c4
-rw-r--r--tests/test_benchmark.py55
-rw-r--r--tests/test_browser.py111
-rw-r--r--tests/test_core.py2
-rw-r--r--tests/test_html5_fullscreen.c23
-rw-r--r--tests/test_html5_mouse.c159
-rw-r--r--tests/test_interactive.py94
-rw-r--r--tests/test_other.py106
-rw-r--r--tests/test_sockets.py66
-rw-r--r--tools/file_packager.py16
-rw-r--r--tools/js-optimizer.js229
-rw-r--r--tools/profile_stripper.py43
-rw-r--r--tools/profile_used.py17
-rw-r--r--tools/shared.py9
-rw-r--r--tools/system_libs.py17
-rw-r--r--tools/test-js-optimizer-asm-outline1-output.js39
-rw-r--r--tools/test-js-optimizer-si-output.js197
-rw-r--r--tools/test-js-optimizer-si.js258
62 files changed, 8383 insertions, 304 deletions
diff --git a/AUTHORS b/AUTHORS
index 076ce1cb..03abe143 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -10,7 +10,7 @@ a license to everyone to use it as detailed in LICENSE.)
* Sigmund Vik <sigmund_vik@yahoo.com>
* Jeff Terrace <jterrace@gmail.com>
* Benoit Tremblay <benoit.tremblay@frimastudio.com>
-* Andreas Bergmeier <andreas.bergmeier@gmx.net>
+* Andreas Bergmeier <abergmeier@gmx.net>
* Ben Schwartz <bens@alum.mit.edu>
* David Claughton <dave@eclecticdave.com>
* David Yip <yipdw@member.fsf.org>
@@ -130,4 +130,6 @@ a license to everyone to use it as detailed in LICENSE.)
* Nicolas Peri <nicox@shivaengine.com> (copyright owned by ShiVa Technologies, SAS)
* Bernhard Fey <e-male@web.de>
* Dave Nicponski <dave.nicponski@gmail.com>
+* Jonathan Jarri <noxalus@gmail.com>
+* Daniele Di Proietto <daniele.di.proietto@gmail.com>
diff --git a/ChangeLog b/ChangeLog
index fea67ddd..328e3cac 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -10,7 +10,28 @@ Not all changes are documented here. In particular, new features, user-oriented
Current trunk code
------------------
- To see a list of commits in the active development branch 'incoming', which have not yet been packaged in a release, see
- https://github.com/kripken/emscripten/compare/1.12.3...incoming
+ https://github.com/kripken/emscripten/compare/1.13.1...incoming
+
+v1.13.1: 3/10/2014
+------------------
+ - Disallow C implicit function declarations by making it an error instead of a warning by default. These will not work with Emscripten, due to strict Emscripten signature requirements when calling function pointers (#2175).
+ - Allow transitioning to full screen from SDL as a response to mouse press events.
+ - Fixed a bug in previous 1.13.0 release that broke fullscreen transitioning from working.
+ - Fixed emscripten/html5.h to be used in C source files.
+ - Fix an issue where extraneous system libraries would get included in the generated output (#2191).
+ - Added a new function emscripten_async_wget2_data() that allows reading from an XMLHTTPRequest directly into memory while supporting advanced features.
+ - Fixed esc key code in GLFW.
+ - Full list of changes: https://github.com/kripken/emscripten/compare/1.13.0...1.13.1
+
+v1.13.0: 3/3/2014
+------------------
+ - Fixed the deprecated source mapping syntax warning.
+ - Fixed a buffer overflow issue in emscripten_get_callstack (#2171).
+ - Added support for -Os (optimize for size) and -Oz (aggressively optimize for size) arguments to emcc.
+ - Fixed a typo that broko the call signature of glCompressedTexSubImage2D() function (#2173).
+ - Added new browser fullscreen resize logic that always retains aspect ratio and adds support for IE11.
+ - Improve debug messaging with bad function pointer calls when -s ASSERTIONS=2 is set.
+ - Full list of changes: https://github.com/kripken/emscripten/compare/1.12.3...1.13.0
v1.12.3: 2/27/2014
------------------
diff --git a/README.md b/README.md
index 6b4778bf..8f0d244e 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-![emscripten logo](media/switch_logo.jpeg)
+![emscripten logo](media/switch_logo.png)
Emscripten is an LLVM-to-JavaScript compiler. It takes LLVM bitcode - which can be generated
from C/C++, using `llvm-gcc` (DragonEgg) or `clang`, or any other language that can be
diff --git a/emcc b/emcc
index f7d1a939..4ba63f21 100755
--- a/emcc
+++ b/emcc
@@ -235,6 +235,13 @@ Options that are modified or new in %s include:
slower because JS optimization will be
limited to 1 core. (default in -O0)
+ -profiling Use reasonable defaults when emitting JS to
+ make the build useful for profiling. This
+ sets -g2 (preserve function names) and may
+ also enable optimizations that affect
+ performance and otherwise might not be
+ performed in -g2.
+
--typed-arrays <mode> 0: No typed arrays
1: Parallel typed arrays
2: Shared (C-like) typed arrays (default)
@@ -470,7 +477,16 @@ Options that are modified or new in %s include:
1: Emit a separate memory initialization file
in binary format. This is more efficient than
storing it as text inside JavaScript, but does
- mean you have another file to publish.
+ mean you have another file to publish. The
+ binary file will also be loaded asynchronously,
+ which means main() will not be called until
+ the file is downloaded and applied; you cannot
+ call any C functions until it arrives. (Call
+ yourself from main() to know when all async
+ stuff has happened and it is safe to call
+ library functions, as main() will only be
+ called at that time. You can also call
+ addOnPreMain from a preRun.)
-Wno-warn-absolute-paths If not specified, the compiler will warn about any
uses of absolute paths in -I and -L command line
@@ -761,6 +777,7 @@ try:
opt_level = 0
debug_level = 0
+ profiling = False
js_opts = None
llvm_opts = None
llvm_lto = None
@@ -881,6 +898,10 @@ try:
requested_level = newargs[i][2:] or '3'
debug_level = validate_arg_level(requested_level, 4, 'Invalid debug level: ' + newargs[i])
newargs[i] = '-g' # we'll need this to get LLVM debug info
+ elif newargs[i] == '-profiling':
+ debug_level = 2
+ profiling = True
+ newargs[i] = ''
elif newargs[i] == '--bind':
bind = True
newargs[i] = ''
@@ -1025,9 +1046,6 @@ try:
if DEBUG: start_time = time.time() # done after parsing arguments, which might affect debug state
- if closure:
- assert os.path.exists(shared.CLOSURE_COMPILER), logging.error('fatal: Closure compiler (%s) does not exist', shared.CLOSURE_COMPILER)
-
for i in range(len(newargs)):
if newargs[i] == '-s':
if is_minus_s_for_emcc(newargs, i):
@@ -1181,7 +1199,7 @@ try:
shared.Settings.ASM_JS = 1 if opt_level > 0 else 2
try:
assert shared.Settings.UNALIGNED_MEMORY == 0, 'forced unaligned memory not supported in fastcomp'
- assert shared.Settings.CHECK_HEAP_ALIGN == 0, 'check heap align not supported in fastcomp yet'
+ assert shared.Settings.CHECK_HEAP_ALIGN == 0, 'check heap align not supported in fastcomp - use SAFE_HEAP instead'
assert shared.Settings.SAFE_DYNCALLS == 0, 'safe dyncalls not supported in fastcomp'
assert shared.Settings.ASM_HEAP_LOG == 0, 'asm heap log not supported in fastcomp'
assert shared.Settings.LABEL_DEBUG == 0, 'label debug not supported in fastcomp'
@@ -1243,6 +1261,10 @@ try:
logging.warning('disabling closure because debug info was requested')
closure = False
+ if closure:
+ shared.Settings.CLOSURE_COMPILER = 1
+ assert os.path.exists(shared.CLOSURE_COMPILER), logging.error('fatal: Closure compiler (%s) does not exist', shared.CLOSURE_COMPILER)
+
assert shared.LLVM_TARGET in shared.COMPILER_OPTS
if shared.LLVM_TARGET == 'i386-pc-linux-gnu':
shared.Settings.TARGET_X86 = 1
@@ -1369,6 +1391,7 @@ try:
if file_ending.endswith(SOURCE_ENDINGS):
temp_file = temp_files[i]
logging.debug('optimizing %s', input_file)
+ #if DEBUG: shutil.copyfile(temp_file, os.path.join(TEMP_DIR, 'to_opt.bc') # useful when LLVM opt aborts
shared.Building.llvm_opt(temp_file, llvm_opts)
# If we were just asked to generate bitcode, stop there
@@ -1487,7 +1510,7 @@ try:
# Prepare .ll for Emscripten
if not LEAVE_INPUTS_RAW:
- if save_bc:
+ if save_bc and not fastcomp:
final = shared.Building.llvm_dis(final, final + '.ll')
else:
assert len(input_files) == 1
@@ -1666,6 +1689,10 @@ try:
js_optimizer_queue += ['simplifyExpressions']
+ # simplify ifs if it is ok to make the code somewhat unreadable, and unless outlining (simplified ifs
+ # with commaified code breaks late aggressive variable elimination)
+ if shared.Settings.SIMPLIFY_IFS and (debug_level == 0 or profiling) and shared.Settings.OUTLINING_LIMIT == 0: js_optimizer_queue += ['simplifyIfs']
+
if opt_level >= 3 and shared.Settings.PRECISE_F32: js_optimizer_queue += ['optimizeFrounds']
if closure and not shared.Settings.ASM_JS:
diff --git a/emscripten-version.txt b/emscripten-version.txt
index bf2b5ccf..2e46fa6f 100644
--- a/emscripten-version.txt
+++ b/emscripten-version.txt
@@ -1,2 +1,2 @@
-1.13.0
+1.13.2
diff --git a/media/powered_by_logo.png b/media/powered_by_logo.png
new file mode 100644
index 00000000..69e90c77
--- /dev/null
+++ b/media/powered_by_logo.png
Binary files differ
diff --git a/media/powered_by_logo.svg b/media/powered_by_logo.svg
new file mode 100644
index 00000000..f39123c1
--- /dev/null
+++ b/media/powered_by_logo.svg
@@ -0,0 +1,1547 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+
+<svg
+ xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ id="Layer_1"
+ x="0px"
+ y="0px"
+ width="900px"
+ height="400px"
+ viewBox="0 0 900 400"
+ enable-background="new 0 0 900 400"
+ xml:space="preserve"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="emscripten_powered_by_logo.svg"><metadata
+ id="metadata345"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+ id="defs343"><linearGradient
+ y2="247.6265"
+ x2="225.1929"
+ y1="152.499"
+ x1="225.1929"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient5104">
+ <stop
+ id="stop5106"
+ style="stop-color:#C1D72F"
+ offset="0.3227531" />
+ <stop
+ id="stop5108"
+ style="stop-color:#BCD631"
+ offset="0.45119295" />
+ <stop
+ id="stop5110"
+ style="stop-color:#AFD136"
+ offset="0.64491969" />
+ <stop
+ id="stop5112"
+ style="stop-color:#ABD037"
+ offset="1" />
+ <a:midPointStop
+ style="stop-color:#C1D72F"
+ offset="0.0123" />
+ <a:midPointStop
+ style="stop-color:#C1D72F"
+ offset="0.3086" />
+ <a:midPointStop
+ style="stop-color:#ABD037"
+ offset="1" />
+ </linearGradient><linearGradient
+ inkscape:collect="always"
+ xlink:href="#SVGID_2_"
+ id="linearGradient5120"
+ x1="397.56918"
+ y1="128.12726"
+ x2="397.56918"
+ y2="166.25996"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.103059,0,0,1.103059,-38.997823,3.1312145)" /><filter
+ inkscape:collect="always"
+ id="filter5126"><feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.56377237"
+ id="feGaussianBlur5128" /></filter><linearGradient
+ inkscape:collect="always"
+ xlink:href="#SVGID_2_"
+ id="linearGradient5134"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.103059,0,0,1.103059,-38.997823,3.1312145)"
+ x1="397.56918"
+ y1="128.12726"
+ x2="397.56918"
+ y2="166.25996" /></defs><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1440"
+ inkscape:window-height="838"
+ id="namedview341"
+ showgrid="false"
+ inkscape:zoom="0.63555556"
+ inkscape:cx="224.82424"
+ inkscape:cy="-52.085109"
+ inkscape:window-x="-8"
+ inkscape:window-y="-8"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="Layer_1" />
+<g
+ id="g5130"
+ transform="matrix(0.91591318,0,0,0.91591318,28.176953,14.143571)"><path
+ transform="matrix(1.103059,0,0,1.103059,-35.073492,-16.03923)"
+ id="path5122"
+ style="fill:#383838;fill-opacity:0.34705882;stroke:none;filter:url(#filter5126)"
+ d="m 494.39333,173.6323 c 0.57407,0.28703 1.87073,1.00226 2.89426,1.02855 0.55732,0.0143 1.14006,-0.1672 1.60262,-0.4784 1.20466,-0.81046 2.23561,-2.03031 2.72683,-3.39661 0.19424,-0.54027 0.0238,-1.72222 0.0238,-1.72222 l -3.82713,-14.06478 -1.98533,0 0.50231,-2.67891 6.36261,0 2.55939,12.22285 4.78392,-9.68746 -2.00924,0 0,-2.65498 7.19979,0 -11.00301,22.38875 -1.69829,1.91358 -2.29628,1.3395 -2.46371,0.26312 -2.29628,-0.21528 -2.79859,-1.36342 z m -12.0637,-14.56445 c -0.93698,1.88565 -1.70261,4.35262 -0.81842,6.26333 0.36549,0.78976 1.35098,1.19428 2.192,1.41737 0.60934,0.16133 1.29167,0.0999 1.88775,-0.10468 0.48126,-0.1655 0.8829,-0.5224 1.255,-0.8697 0.40341,-0.3768 0.77723,-0.80461 1.03505,-1.29262 0.21864,-0.41395 0.40236,-0.84786 0.49325,-1.30698 0.20667,-1.0485 0.35879,-2.1079 0.33583,-3.17631 -0.0184,-0.87403 -0.0789,-1.87107 -0.47711,-2.64959 -0.26344,-0.51379 -0.77017,-0.71849 -1.33113,-0.85633 -0.42395,-0.10479 -0.81432,-0.0626 -1.21773,0.10517 -0.65479,0.27273 -1.2544,0.5311 -1.82112,0.95764 -0.57331,0.4317 -1.21403,0.86959 -1.53337,1.5127 z m 0.65588,-4.31208 c 0,0 2.19341,-1.80738 3.45549,-2.27082 0.71718,-0.26365 3.45363,-0.65258 4.15,-0.3378 1.47292,0.66633 2.26103,1.57529 2.7222,2.60001 0.46118,1.02472 0.69944,2.59956 0.79701,3.73627 0.13278,1.55027 -0.13682,3.77629 -0.53404,5.74843 -0.30079,1.49256 -1.01883,2.74423 -1.83478,3.92156 -1.06526,1.5373 -1.82382,2.15116 -3.66756,2.46594 -0.98864,0.16889 -1.93845,0.46787 -3.25466,0.0928 -1.4384,-0.40963 -2.35273,-0.81244 -3.39599,-1.63337 -0.72524,-0.57054 -1.16043,-1.54043 -1.16043,-1.54043 l 0,2.82636 -4.8903,0 3.39872,-23.01602 -1.92242,-0.85888 0.0403,-2.38127 7.25847,0.0534 z m -23.77803,2.20447 c 0.29175,1.49273 0.0813,4.83252 -0.86111,6.69751 -0.3062,0.60617 -0.94813,1.32967 -1.55479,1.6983 -1.01515,0.61713 -2.21688,1.21322 -3.3966,1.07639 -0.47944,-0.0541 -0.97036,-0.34348 -1.24383,-0.74151 -0.47686,-0.69328 -0.43621,-1.55032 -0.45448,-2.39198 -0.024,-1.06873 0.13137,-2.23775 0.38272,-3.277 0.18705,-0.7744 0.4229,-1.58254 0.86111,-2.24844 0.39037,-0.59323 0.92628,-1.12617 1.55478,-1.45909 0.54854,-0.29014 1.19695,-0.38467 1.81791,-0.40664 0.63637,-0.0231 1.3031,0.0385 1.88966,0.28704 0.3875,0.16453 0.92361,0.3524 1.00463,0.76542 z m 1.29312,-9.69052 -0.64254,6.12262 c 0,0 -1.68393,-0.96858 -2.605,-1.25148 -0.73032,-0.22434 -1.50312,-0.36654 -2.26624,-0.33838 -0.97069,0.0345 -1.91182,0.22099 -2.81751,0.57088 -0.9185,0.35497 -1.78344,0.94565 -2.49338,1.62792 -0.88025,0.84538 -1.51404,1.90455 -2.02977,3.0106 -0.39653,0.84993 -0.69517,1.75284 -0.87975,2.67232 -0.22875,1.14241 -0.44415,2.38719 -0.43937,3.55197 0.01,1.44865 0.0623,2.89489 0.54092,4.26214 0.25525,0.72907 0.71643,1.40578 1.28572,1.9283 0.56835,0.52207 1.29566,0.87604 2.02935,1.11621 0.41072,0.13491 0.85346,0.17274 1.28579,0.16935 1.00285,-0.01 2.03715,-0.0883 2.97671,-0.43999 0.66497,-0.2489 1.21759,-0.73399 1.79298,-1.1502 0.75304,-0.54475 2.16476,-1.86006 2.16476,-1.86006 l 0,1.62374 -0.5751,0 0,1.48807 6.86709,0 0,-2.84135 -1.92841,0 3.21374,-23.57782 -7.37422,0 0,2.33412 z m -93.60062,7.55781 2.33363,15.57933 6.23084,0 4.04243,-11.34169 1.62654,11.34169 5.88425,0 7.05633,-16.38872 0,-2.0141 -6.1713,0 0,2.82349 1.88966,0 -4.04243,10.16973 -0.74151,0 -1.29167,-12.55773 -5.38194,0 -4.7361,12.50989 -1.55478,-12.94538 -6.86496,0 0,2.82349 z m -12.15,0.72146 c -0.56264,0.0892 -1.03524,0.17358 -1.53086,0.45447 -0.737,0.41808 -1.46132,0.95771 -1.91357,1.67437 -0.44123,0.70048 -0.53204,1.57581 -0.66975,2.39196 -0.1751,1.04003 -0.20064,2.10306 -0.19136,3.15741 0.01,0.81614 -0.0138,1.66577 0.35879,2.39197 0.1904,0.37315 0.52874,0.80945 0.88503,1.02855 0.56015,0.34453 1.06632,0.55494 1.72222,0.598 0.72597,0.0483 1.48801,-0.18852 2.10493,-0.57408 0.59422,-0.37072 1.03334,-0.97401 1.38735,-1.5787 0.46117,-0.78744 0.70905,-1.69257 0.90895,-2.58334 0.20377,-0.90704 0.33579,-1.84565 0.28703,-2.77468 -0.0491,-0.92714 -0.18211,-1.88434 -0.57407,-2.72684 -0.2728,-0.58681 -0.70954,-1.00753 -1.29166,-1.29165 -0.44403,-0.21628 -0.99455,-0.24402 -1.48303,-0.16744 z m -6.62442,-0.73581 c 0.65404,-0.6664 1.4072,-1.25479 2.23273,-1.69161 1.0305,-0.54505 2.16429,-0.92749 3.31518,-1.11604 1.51307,-0.24806 3.09342,-0.2847 4.60036,0 0.88055,0.16632 1.78322,0.44742 2.50307,0.98113 0.77409,0.57312 1.35279,1.40936 1.79291,2.26639 0.42901,0.83457 0.6828,1.77223 0.77798,2.70605 0.16564,1.61985 0.024,3.29135 -0.37201,4.87103 -0.33328,1.33759 -0.88436,2.64754 -1.65745,3.78889 -0.67549,0.99679 -1.52894,1.91262 -2.53721,2.5709 -0.89957,0.58746 -1.9718,0.87641 -3.01035,1.15006 -0.87153,0.22963 -1.77166,0.4095 -2.67235,0.40576 -1.21068,-0.01 -2.47998,-0.0817 -3.58589,-0.57511 -1.09854,-0.48896 -1.89728,-1.32739 -2.60455,-2.30013 -0.61123,-0.83995 -1.02561,-1.59975 -1.31932,-2.87516 -0.2125,-0.9233 -0.40006,-2.19912 -0.37215,-3.14592 0.0335,-1.16537 0.3568,-2.74121 0.83416,-3.80434 0.52547,-1.17098 1.17609,-2.3161 2.07489,-3.2319 z m 94.95184,13.82318 c -2.20516,1.01761 -4.61429,1.69636 -7.02343,1.69636 -5.32726,0 -7.22678,-3.12145 -7.22678,-7.22678 0,-7.1251 4.54685,-11.19645 10.0772,-11.19645 3.7324,0 5.56453,1.69625 5.56453,4.47856 0,4.85189 -5.12329,6.27735 -10.41633,6.82001 0.10168,1.73076 0.81446,3.32485 3.3592,3.32485 1.2218,0 2.88401,-0.37315 4.91982,-1.22099 z m -3.22292,-11.77374 c 0,-0.81423 -0.57695,-1.28891 -1.62876,-1.28891 -1.89988,0 -3.46041,1.66212 -3.96978,4.34287 1.45897,-0.20368 5.59854,-0.91613 5.59854,-3.05396 z m -30.33408,11.77374 c -2.2054,1.01761 -4.61457,1.69636 -7.02371,1.69636 -5.32653,0 -7.22671,-3.12145 -7.22671,-7.22678 0,-7.1251 4.54679,-11.19645 10.07785,-11.19645 3.73175,0 5.56382,1.69625 5.56382,4.47856 0,4.85189 -5.12273,6.27735 -10.41568,6.82001 0.10142,1.73076 0.81422,3.32485 3.35884,3.32485 1.22158,0 2.8842,-0.37315 4.91994,-1.22099 z m -3.22305,-11.77374 c 0,-0.81423 -0.57638,-1.28891 -1.62883,-1.28891 -1.89959,0 -3.46023,1.66212 -3.96971,4.34287 1.4591,-0.20368 5.59854,-0.91613 5.59854,-3.05396 z m -82.36051,20.5268 -0.0679,-0.13571 0.98406,-5.66614 2.10303,-15.16698 c 0.0687,-0.40664 -0.0332,-0.61046 -0.30522,-0.71214 l -1.66259,-0.61111 0.37379,-2.57855 6.78556,0 -0.40663,2.71427 0.10142,0.0335 c 2.0016,-1.86631 4.10566,-3.08743 6.24306,-3.08743 2.91821,0 4.95366,1.86577 4.95366,6.78561 0,4.68241 -1.83206,11.6379 -8.14271,11.6379 -2.20534,0 -3.42694,-0.84825 -4.68256,-1.73039 l -0.74621,5.08917 c -0.0341,0.37361 0.0326,0.50898 0.47457,0.54273 l 3.42697,0.33969 -0.37385,2.5447 -9.0589,0 z m 6.78613,-12.04485 c 0.84787,0.71258 1.96788,1.32305 3.22348,1.32305 2.74798,0 3.76601,-3.86811 3.76601,-6.85368 0,-2.002 -0.47476,-3.32542 -1.76432,-3.32542 -1.35696,0 -3.08763,1.4591 -4.30913,2.54506 z m 81.08934,4.85147 0.33969,-2.54464 1.56064,-0.2038 c 0.47498,-0.0683 0.5429,-0.1695 0.61084,-0.67837 l 1.42466,-10.34864 c 0.0335,-0.37315 -0.0335,-0.61046 -0.33914,-0.71214 l -1.69691,-0.61111 0.37365,-2.57855 6.71797,0 -0.44097,3.05395 0.10191,0.0679 c 1.32326,-1.89982 3.22359,-3.46042 5.39485,-3.46042 0.7463,0 2.0359,0.13582 2.61295,0.30538 l -0.84863,6.17508 -3.96972,-0.13582 -0.10157,-1.76443 c -0.0335,-0.30537 -0.10223,-0.40701 -0.37391,-0.40701 -0.64452,0 -1.69636,0.78027 -2.64651,1.76455 l -1.18674,8.61817 c -0.0687,0.54303 -0.0334,0.64474 0.47477,0.67874 l 3.22351,0.27142 -0.37384,2.51081 -10.8575,0 z"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cssscccccccccccccccccssssssssccssscssssscsssccccccccsssssssssccsccsssssssssscsscccccccccccccccccccccccccccccccsssscsssssscscsssssssscsssssssssscsssscsccsscscsssscsccsscsccccccccccsssccccccccssscccccccccccccsccccsccccccc" /><path
+ sodipodi:nodetypes="cssscccccccccccccccccssssssssccssscssssscsssccccccccsssssssssccsccsssssssssscsscccccccccccccccccccccccccccccccsssscsssssscscsssssssscsssssssssscsssscsccsscscsssscsccsscsccccccccccsssccccccccssscccccccccccccsccccsccccccc"
+ inkscape:connector-curvature="0"
+ d="m 509.55935,174.26011 c 0.63327,0.31663 2.06355,1.10555 3.19256,1.13455 0.61476,0.0158 1.25757,-0.18443 1.76781,-0.5277 1.3288,-0.89397 2.46618,-2.23946 3.00784,-3.74661 0.21419,-0.59598 0.0258,-1.89972 0.0258,-1.89972 l -4.22153,-15.51428 -2.18993,0 0.55406,-2.95501 7.01835,0 2.82313,13.48255 5.27696,-10.68586 -2.21631,0 0,-2.92858 7.94179,0 -12.13698,24.69605 -1.87332,2.11078 -2.5329,1.4776 -2.71762,0.29022 -2.53295,-0.23748 -3.08699,-1.50392 z m -13.30698,-16.06545 c -1.0335,2.08005 -1.87803,4.80122 -0.90274,6.90883 0.4032,0.87116 1.49018,1.31738 2.4179,1.56347 0.67214,0.17793 1.42477,0.1102 2.08233,-0.11548 0.53084,-0.1826 0.97383,-0.5762 1.38432,-0.9593 0.44502,-0.4157 0.85733,-0.8875 1.14176,-1.42582 0.24113,-0.45665 0.44375,-0.93526 0.54404,-1.44168 0.22797,-1.1566 0.3958,-2.3252 0.37043,-3.50371 -0.0204,-0.96413 -0.0869,-2.06387 -0.52631,-2.92259 -0.29054,-0.56679 -0.84946,-0.79259 -1.46826,-0.94463 -0.46761,-0.11559 -0.89829,-0.0686 -1.34322,0.11597 -0.72226,0.30083 -1.38368,0.5859 -2.00879,1.05634 -0.63242,0.4762 -1.33915,0.9593 -1.69146,1.6686 z m 0.72346,-4.75648 c 0,0 2.41951,-1.99358 3.81169,-2.50482 0.79109,-0.29085 3.80953,-0.71977 4.57766,-0.3726 1.6247,0.73503 2.49408,1.73759 3.00274,2.86791 0.50868,1.13043 0.77154,2.86756 0.87911,4.12137 0.14648,1.71007 -0.15092,4.16549 -0.58904,6.34083 -0.33179,1.64636 -1.12383,3.02703 -2.02388,4.32576 -1.17506,1.6957 -2.01178,2.37286 -4.04556,2.72004 -1.09051,0.18629 -2.13814,0.51607 -3.59006,0.10268 -1.5866,-0.45183 -2.59522,-0.89615 -3.74599,-1.8017 -0.79994,-0.62933 -1.28003,-1.6992 -1.28003,-1.6992 l 0,3.11766 -5.39426,0 3.74898,-25.38802 -2.12052,-0.94738 0.0443,-2.62669 8.00657,0.0587 z m -26.22853,2.43167 c 0.32185,1.64663 0.0893,5.33062 -0.9498,7.38781 -0.33781,0.66857 -1.04588,1.46667 -1.7151,1.8733 -1.11975,0.68073 -2.44527,1.33822 -3.7466,1.18729 -0.52883,-0.0601 -1.07036,-0.37888 -1.37203,-0.81791 -0.52601,-0.76478 -0.48121,-1.71012 -0.50128,-2.63848 -0.0263,-1.17893 0.14487,-2.46835 0.42212,-3.6147 0.20635,-0.8543 0.4665,-1.74564 0.94981,-2.48024 0.43067,-0.65433 1.02178,-1.24217 1.71508,-1.60939 0.60504,-0.32004 1.32025,-0.42437 2.00521,-0.44854 0.70197,-0.0251 1.4374,0.0425 2.08446,0.31654 0.4274,0.18153 1.01882,0.3888 1.10813,0.84432 z m 1.42642,-10.68922 -0.70874,6.75362 c 0,0 -1.85753,-1.06838 -2.8735,-1.38048 -0.80562,-0.24744 -1.65802,-0.40424 -2.49984,-0.37318 -1.07069,0.0382 -2.10882,0.24369 -3.1078,0.62968 -1.01321,0.39157 -1.96724,1.04315 -2.75039,1.79572 -0.97095,0.93248 -1.67003,2.10085 -2.23897,3.3208 -0.43738,0.93753 -0.76677,1.93354 -0.9704,2.94777 -0.2523,1.26016 -0.4899,2.63324 -0.48461,3.91802 0.011,1.59795 0.0683,3.19329 0.59661,4.70144 0.28155,0.80417 0.79028,1.55058 1.41822,2.127 0.62695,0.57587 1.4292,0.96634 2.23856,1.23121 0.45301,0.14881 0.94135,0.19054 1.41828,0.18685 1.10615,-0.011 2.24705,-0.0973 3.28346,-0.48539 0.73352,-0.2745 1.34304,-0.80959 1.97773,-1.2687 0.83064,-0.60085 2.38786,-2.05176 2.38786,-2.05176 l 0,1.79104 -0.63429,0 0,1.64147 7.57478,0 0,-3.13415 -2.12721,0 3.54494,-26.00772 -8.13411,0 0,2.57462 z m -103.24702,8.33671 2.57413,17.18493 6.87304,0 4.45903,-12.51049 1.79414,12.51049 6.49065,0 7.78353,-18.07772 0,-2.2217 -6.8073,0 0,3.11449 2.08446,0 -4.45903,11.21783 -0.8179,0 -1.42488,-13.85193 -5.93654,0 -5.2242,13.79919 -1.71497,-14.27958 -7.57246,0 0,3.11449 z m -13.4021,0.79586 c -0.62064,0.0982 -1.14194,0.19148 -1.68866,0.50127 -0.813,0.46118 -1.61192,1.05641 -2.11077,1.84697 -0.48673,0.77268 -0.58683,1.73821 -0.73875,2.63846 -0.1932,1.14723 -0.22134,2.31976 -0.21116,3.48281 0.011,0.90024 -0.0148,1.83747 0.39579,2.63847 0.21,0.41165 0.58324,0.89285 0.97623,1.13455 0.61796,0.38003 1.17622,0.61214 1.89972,0.6596 0.80077,0.0533 1.64141,-0.20792 2.32189,-0.63318 0.65546,-0.40892 1.13978,-1.07441 1.53029,-1.7414 0.50878,-0.86864 0.78215,-1.86707 1.00265,-2.84964 0.22477,-1.00044 0.37039,-2.03585 0.31663,-3.06058 -0.0541,-1.02274 -0.20091,-2.07854 -0.63327,-3.00784 -0.3009,-0.64731 -0.78264,-1.11143 -1.42476,-1.42485 -0.48983,-0.23858 -1.09705,-0.26912 -1.63583,-0.18464 z m -7.30711,-0.81171 c 0.72143,-0.735 1.55219,-1.38409 2.46282,-1.86591 1.1367,-0.60125 2.38729,-1.02309 3.65678,-1.23104 1.66908,-0.27366 3.41222,-0.314 5.07446,0 0.97135,0.18342 1.96702,0.49352 2.76107,1.08223 0.85389,0.63222 1.49219,1.55466 1.97771,2.49999 0.47321,0.92057 0.7531,1.95483 0.85808,2.98495 0.18274,1.78675 0.0263,3.63055 -0.41031,5.37303 -0.36757,1.47539 -0.97545,2.92034 -1.82825,4.17929 -0.74509,1.09959 -1.68654,2.10982 -2.79871,2.8359 -0.99227,0.64796 -2.175,0.96671 -3.32055,1.26856 -0.96139,0.25333 -1.95426,0.4517 -2.94774,0.44756 -1.33549,-0.011 -2.73559,-0.0897 -3.9555,-0.63431 -1.21174,-0.53936 -2.09278,-1.46419 -2.87295,-2.53723 -0.67423,-0.92645 -1.13131,-1.76457 -1.45532,-3.17146 -0.2344,-1.0184 -0.44126,-2.42572 -0.41044,-3.47012 0.0365,-1.28547 0.39349,-3.02371 0.92005,-4.19644 0.57967,-1.29168 1.29729,-2.5548 2.2888,-3.565 z m 104.73744,15.24778 c -2.43247,1.12251 -5.0899,1.87126 -7.74734,1.87126 -5.87626,0 -7.97147,-3.44315 -7.97147,-7.97158 0,-7.8594 5.0154,-12.35035 11.11569,-12.35035 4.11711,0 6.13803,1.87105 6.13803,4.94016 0,5.35189 -5.65129,6.92425 -11.48983,7.52281 0.11219,1.90916 0.89836,3.66755 3.7054,3.66755 1.3477,0 3.18121,-0.41165 5.42682,-1.34689 z m -3.55513,-12.98704 c 0,-0.89823 -0.63635,-1.42181 -1.79655,-1.42181 -2.09568,0 -3.81712,1.83342 -4.37899,4.79047 1.60937,-0.22468 6.17554,-1.01053 6.17554,-3.36866 z m -33.46028,12.98704 c -2.4327,1.12251 -5.09006,1.87126 -7.74751,1.87126 -5.87553,0 -7.97151,-3.44315 -7.97151,-7.97158 0,-7.8594 5.01539,-12.35035 11.11645,-12.35035 4.11635,0 6.13722,1.87105 6.13722,4.94016 0,5.35189 -5.65062,6.92425 -11.48908,7.52281 0.11182,1.90916 0.89812,3.66755 3.70494,3.66755 1.34748,0 3.1815,-0.41165 5.42704,-1.34689 z m -3.55514,-12.98704 c 0,-0.89823 -0.63578,-1.42181 -1.79674,-1.42181 -2.09539,0 -3.81683,1.83342 -4.37881,4.79047 1.60951,-0.22468 6.17555,-1.01053 6.17555,-3.36866 z m -90.84852,22.6422 -0.0749,-0.14971 1.08546,-6.25004 2.31984,-16.73008 c 0.0757,-0.44854 -0.0367,-0.67336 -0.33673,-0.78554 l -1.83388,-0.67411 0.41228,-2.84425 7.48486,0 -0.44853,2.99397 0.11182,0.0371 c 2.2079,-2.05871 4.52887,-3.40563 6.88646,-3.40563 3.21901,0 5.46427,2.05807 5.46427,7.48491 0,5.16501 -2.02094,12.8373 -8.98192,12.8373 -2.43264,0 -3.78014,-0.93565 -5.16516,-1.90869 l -0.82311,5.61357 c -0.0376,0.41212 0.0356,0.56148 0.52347,0.59873 l 3.78017,0.37469 -0.41234,2.8069 -9.9925,0 z m 7.48553,-13.28615 c 0.93528,0.78598 2.17068,1.45946 3.55568,1.45946 3.03118,0 4.15411,-4.26682 4.15411,-7.56009 0,-2.2083 -0.52366,-3.66812 -1.94612,-3.66812 -1.49686,0 -3.40583,1.6095 -4.75323,2.80736 z m 89.44624,5.35147 0.37469,-2.80694 1.72154,-0.2248 c 0.52388,-0.0753 0.5988,-0.1869 0.67374,-0.74827 l 1.57152,-11.41514 c 0.0365,-0.41155 -0.0368,-0.67336 -0.3741,-0.78554 l -1.87181,-0.67411 0.41215,-2.84425 7.41037,0 -0.48647,3.36865 0.11241,0.0749 c 1.45966,-2.09562 3.55581,-3.81702 5.95085,-3.81702 0.8232,0 2.2457,0.14982 2.88225,0.33688 l -0.93613,6.81148 -4.37882,-0.14982 -0.11196,-1.94633 c -0.0371,-0.33677 -0.11284,-0.44891 -0.41252,-0.44891 -0.71092,0 -1.87116,0.86067 -2.91921,1.94635 l -1.30904,9.50637 c -0.0757,0.59903 -0.0368,0.71124 0.52367,0.74874 l 3.55571,0.29932 -0.41234,2.76961 -11.9765,0 z"
+ style="fill:url(#linearGradient5134);fill-opacity:1;stroke:none"
+ id="path5080" /></g><path
+ fill="#E2E2E2"
+ d="M256.023,135.437H196.36c-16.432,0-29.8,13.368-29.8,29.8v73.527c0,16.432,13.368,29.8,29.8,29.8h59.663 c16.433,0,29.801-13.368,29.801-29.8v-73.527C285.824,148.805,272.456,135.437,256.023,135.437z M191.561,165.236 c0-2.646,2.153-4.8,4.8-4.8h59.663c2.647,0,4.801,2.153,4.801,4.8v73.527c0,2.646-2.153,4.8-4.801,4.8H196.36 c-2.646,0-4.8-2.153-4.8-4.8V165.236z"
+ id="path3" />
+<path
+ d="m 531.664,250.155 h 18.498 l -2.809,18.064 h 5.59 37.586 l 2.6,-17.718 c 4.98,-1.091 9.133,-3.455 12.512,-6.693 3.084,4.075 8.566,7.37 18.252,7.37 6.338,0 12.775,-1.807 17.174,-3.687 4.254,2.399 9.463,3.687 15.459,3.687 3.088,0 6.236,-0.355 9.426,-1.023 h 67.135 l 3.354,-24.827 -5.445,-0.764 1.879,-13.356 c 0.371,-2.386 0.449,-4.66 0.449,-6.156 l -0.008,-0.375 c -0.457,-12.191 -8.139,-19.765 -20.045,-19.765 -2.404,0 -4.623,0.314 -6.676,0.852 h -34.189 l -0.035,0.244 c -2.527,-0.701 -5.41,-1.096 -8.686,-1.096 -3.801,0 -7.406,0.555 -10.76,1.598 l 0.105,-0.746 h -12.467 l 1.826,-12.951 H 615.08 l -1.846,7.658 c -1.373,5.704 -2.213,5.793 -4.453,6.03 l -4.508,0.477 c -3.049,-1.424 -6.357,-2.065 -9.602,-2.065 -2.135,0 -4.275,0.284 -6.416,0.852 h -19.291 c 0.502,-1.772 0.775,-3.674 0.775,-5.678 0,-9.601 -6.846,-16.305 -16.646,-16.305 -11.055,0 -18.775,7.721 -18.775,18.776 0,0.951 0.082,1.869 0.219,2.764 -2.135,-0.288 -4.277,-0.409 -5.553,-0.409 -2.053,0 -4.072,0.288 -6.045,0.852 h -31.342 c -2.74,-0.553 -5.641,-0.852 -8.537,-0.852 -7.138,0 -13.492,1.674 -18.808,4.723 l -3.451,-1.461 c -3.711,-1.571 -11.232,-3.262 -18.979,-3.262 -8.933,0 -16.383,2.56 -21.576,7.016 -3.265,-4.473 -8.523,-7.016 -15.228,-7.016 -4.822,0 -9.021,1.477 -12.572,3.44 -2.996,-2.204 -6.796,-3.44 -11.115,-3.44 -2.327,0 -4.48,0.315 -6.476,0.852 h -33.963 l -0.035,0.245 c -2.526,-0.702 -5.41,-1.097 -8.687,-1.097 -20.458,0 -35.307,16.031 -35.307,38.117 0,17.363 10.785,28.149 28.148,28.149 3.087,0 6.236,-0.356 9.426,-1.023 h 88.816 c 3.706,0.676 7.669,1.023 11.154,1.023 8.907,0 16.278,-2.375 21.51,-6.593 4.872,4.252 11.585,6.593 19.728,6.593 3.053,0 6.206,-0.368 9.286,-1.023 h 44.664 2.069 z"
+ id="path5"
+ inkscape:connector-curvature="0"
+ style="fill:#e2e2e2" />
+<path
+ fill="#F5F5F5"
+ d="M255.023,133.437H195.36c-16.432,0-29.8,13.368-29.8,29.8v73.527c0,16.432,13.368,29.8,29.8,29.8h59.663 c16.433,0,29.801-13.368,29.801-29.8v-73.527C284.824,146.805,271.456,133.437,255.023,133.437z M190.561,163.236 c0-2.646,2.153-4.8,4.8-4.8h59.663c2.647,0,4.801,2.153,4.801,4.8v73.527c0,2.646-2.153,4.8-4.801,4.8H195.36 c-2.646,0-4.8-2.153-4.8-4.8V163.236z"
+ id="path7" />
+<g
+ id="g9">
+ <g
+ id="g11">
+ <path
+ fill="#FBFDF8"
+ d="M195.361,251.626c-8.161,0-14.8-6.64-14.8-14.8v-73.527c0-8.161,6.639-14.8,14.8-14.8h59.663 c8.161,0,14.8,6.639,14.8,14.8v73.527c0,8.16-6.639,14.8-14.8,14.8H195.361z"
+ id="path13" />
+ <path
+ fill="#F0F4E1"
+ d="M255.024,152.499c5.964,0,10.8,4.835,10.8,10.8v73.527c0,5.965-4.835,10.8-10.8,10.8h-59.663 c-5.964,0-10.8-4.835-10.8-10.8v-73.527c0-5.964,4.835-10.8,10.8-10.8H255.024 M255.024,144.499h-59.663 c-10.366,0-18.8,8.434-18.8,18.8v73.527c0,10.366,8.434,18.8,18.8,18.8h59.663c10.366,0,18.8-8.434,18.8-18.8v-73.527 C273.824,152.933,265.391,144.499,255.024,144.499L255.024,144.499z"
+ id="path15" />
+ </g>
+ <defs
+ id="defs17">
+ <filter
+ id="Adobe_OpacityMaskFilter"
+ filterUnits="userSpaceOnUse"
+ x="176.562"
+ y="144.499"
+ width="97.263"
+ height="111.127">
+
+ <feColorMatrix
+ type="matrix"
+ values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
+ color-interpolation-filters="sRGB"
+ result="source"
+ id="feColorMatrix20" />
+ </filter>
+ </defs>
+ <mask
+ maskUnits="userSpaceOnUse"
+ x="176.562"
+ y="144.499"
+ width="97.263"
+ height="111.127"
+ id="SVGID_1_">
+ <g
+ filter="url(#Adobe_OpacityMaskFilter)"
+ id="g23">
+
+ <image
+ overflow="visible"
+ width="422"
+ height="480"
+ xlink:href="data:image/jpeg;base64,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 7fGT6fI+nyPp8j6fI+nyPp8j6fI+nyPp8j6fI+nyPp8D7x8fJtxp1m7Tp5jbw/MfWIjbXTk5SHsH j/sBQ63ZK2AdthqO8vXbUZWrJ0V/oJ7ZB7Sa+ofJMIkS6IySyJRLIkSyJEsiRLIkSyJEsiRLIkSy JVLYiRLYicEr8xfwSemN0kjy8PIdkfxQp0xWEAPYPH/YCh1uyVsAAz08ome2si37qZkumaULspIu 2aRkuyki7KSLspIuyki7KSLspIuyki7KSLtilC6qSLtilC6fNNFu5qz8k7wcI+vkAAHsHj/sBWoQ AAAAAAAAAAAAAAAAAAAAAAAAAHpAf//aAAgBAgABBQD/ACi//9oACAEDAAEFAP8AKL//2gAIAQEA AQUA6w6rz/LM+776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++qzvv qs776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++qz vvqs776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++ qzvvqs776rMfLPYHlHyRFUbivuDeUv1FOSVKJkRwE4CcAOAHADgBwA4AcAOAHADgBwA4AcAOAHAD gBwA4AcAOAHADgBwA4AcAOAnARciFyRUK8ndQcgyGxaaqf4fYHlHwRFUjZe68RsqbpGoKIUQkKYY kISEYJDAmBMCYEwJgTAmBMCYEwJgTAmBMCYEwJgTAmBMCYEwJgTAmCQWELCKoSFcNByEhIyxusk5 VVQV0VUL8vYHlGltupyqDlaIMREQaijcUoilMUSKgkZDDIYZDDIYZDDIYZDDIYZDDIYZDDIYZDDI YZDDIYZDDIYZDDIYZDDIYZDDIYZDDIYZDDILGQWKVRSuKORR2KPRCZltDiSYrjFXx9geUaG26nKs vy9KEjxhmONRxuOUMFLAjIjJdF0XRdIXSF0hdF0XRdF0XRdF0XRdF0XRdF0XRdIXSF0hdIXRdCsi sisFTBXHHY49GH4xMhU10y4tTFfw9geUCJ9rlcL6SNHGGBlgbZKGilsShDZQ+kPr/wBX0fSGygtC CtoVNDjI6wPsElgzCGjlLrdTden2B5QZfGvnYbCIkdkYZGmihsSn61ItJXQOtD7JIZJTBm0X6+Hs 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 DNdYwcM11jBwzXWMHDNdYwcM11jBwzXWMHDNdYwcM11jBwzXWMHDP+z/AEd/2MzUhXbvz7H6XVO/ Y7vFJSkpSUpKUlKSlJSkpSUpKUlKSlJSkpSUpKUlKSlJSkpSUpKUlKSlJSkpSUpKUlKSlJSkp+le p3d6ndOn4OZqQrt349EOqp0QT9PVfzGDBgwYMGDBgwYMGDBgwYMGDBgwYMGDBgwYMGDBgwYMGC9X RVh9/wAjo8nRfnmakK7d+H+LqdVEefTqonYYMGDBgwYMGDBgwYMGDBgwYMGDBgwYMGDBgwYMGDBg wYMGDBeqd/uKip2+/wAszUhXbsqOutURVT9SidhgwYMlZ/XsGDBgvYVFQVOnb6fHM1IV27J0QR95 O6idhOwyaWC9hU6d/oK6v0+GZqQrt2RFVOyCdhBJrUU/cRO6N+GZqQrt06CL07qIJNiijydGjzsu ZqQrt0dd/MRBBJsUUU/y+8uZqQrt06/YQQSbFFFOv2WXM1IV26KIJNyij0uZqQrt0e8iCTcoo/Lm akK7dHvIggk2KKKPy5mpCu3R7yIJNyij8uZqQrt0e8iCTcoo/LmakK7dHvIgk3KKPy5mpCu3R7yI JNyij8uZqQrt0e8iCTaooo/LmakK7dHvIgk3KKPy5mpCu3R7yIJNyij8uZqQrt0e8iCCTaoo/Lma kK7dHvIggk2qKPy5mpCu3R7yIIJNqij8uZqQrt0e8iCCTaoo/LmakK7dHvIggk2qKPy5mpCu3R7y IIJNqij8uZqQrt0e8iCCTaoo/LmakK7dHvIggk2qKPy5mpCu3R7yIIJNqij8uZqQrt0e8iCCTaoo /LmakK7dHvIggk2qKPy5mpCu3R7yIIJNqij8uZqQrt0e8iCCTaoo/LmakK7dHvIggk2qKPy5mpCu 3R7yIIJNqij8uZqQrt0e8iCCTaoo/LmakK7dHvIggk2qKPy5mpCu3R7yIIJNqij8uZqQrt0e8iCC Taoo/LmakK7dHvIggk2qKPy5mpCu3R7yIIJNqij8uZqQrt0e8iCTcoo/LmakK7dHvIgk2qKKPy5m pCu3R7yIJNyij8uZqQrt0e8iCTcoo/LmakK7dHvIgk3KKPy5mpCu3R7yIJNyij8uZqQrt0e8iCCT aoo/LmakK7dHvIgk3KKPy5mpCu3R4QSblFHpczUhXboqfcQQSbVFFT7y5mpCu3RPzEEEmxRRRHZc zUhXbojyfRR1RBJsUUUX7JLmakK7dk/bVfAgk2KKL37qwVV+suZqQrt2RHk+giook2L3F7i9GJ8M zUhXbsqItFRFRRO40aNmVo0aL3FhuL5+OZqQrt34I69REVFGjRo0aNGjf6po0aNGjRo0XuK5DXv9 zqrV+OZqQrt349UXt9hEVeijRo0aNGjRo0aNGjRo0aNGjRo0aNGjRo0aNGjRo0aNGjRo0Xq9/YVH V6OnVflmakK7d+XVOw3qh0e7FM7PJ7KQ0aNGjRo0aNGjRo0aNGjRo0aNGjRo0aNGjRpSKaH6V6nR 3sh1eXr+BmakK7d/C7KqFJfZTX2U19lNfZTX2U19lN72U3vZTe9lN72U3vZTe9lN72U3vZTe9lN7 2U3vZTe9lN72U3vZTe9lN72U3vZTe9lN72U19lNfZTX2U19lNfZSX2d3l/Ej/wAj/wCf+7/jD6/y f5/7tBOnX+L/AMfr+5tPKm08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptPK m08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptP Km08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptPKn/jan//Z"
+ transform="matrix(0.24 0 0 0.24 174.5615 142.499)"
+ id="image25">
+ </image>
+ </g>
+ </mask>
+ <g
+ opacity="0.09"
+ mask="url(#SVGID_1_)"
+ a:adobe-blending-mode="multiply"
+ a:adobe-opacity-share="1"
+ id="g27">
+ <path
+ fill="#1D2915"
+ a:adobe-blending-mode="normal"
+ a:adobe-opacity-share="0"
+ d="M195.361,251.626 c-8.161,0-14.8-6.64-14.8-14.8v-73.527c0-8.161,6.639-14.8,14.8-14.8h59.663c8.161,0,14.8,6.639,14.8,14.8v73.527 c0,8.16-6.639,14.8-14.8,14.8H195.361z"
+ id="path29" />
+ <path
+ fill="#1D2915"
+ a:adobe-blending-mode="normal"
+ a:adobe-opacity-share="0"
+ d="M255.024,152.499 c5.964,0,10.8,4.835,10.8,10.8v73.527c0,5.965-4.835,10.8-10.8,10.8h-59.663c-5.964,0-10.8-4.835-10.8-10.8v-73.527 c0-5.964,4.835-10.8,10.8-10.8H255.024 M255.024,144.499h-59.663c-10.366,0-18.8,8.434-18.8,18.8v73.527 c0,10.366,8.434,18.8,18.8,18.8h59.663c10.366,0,18.8-8.434,18.8-18.8v-73.527C273.824,152.933,265.391,144.499,255.024,144.499 L255.024,144.499z"
+ id="path31" />
+ </g>
+</g>
+<g
+ id="g33">
+ <g
+ id="g35">
+ <linearGradient
+ id="SVGID_2_"
+ gradientUnits="userSpaceOnUse"
+ x1="225.1929"
+ y1="152.499"
+ x2="225.1929"
+ y2="247.6265">
+ <stop
+ offset="0.0123"
+ style="stop-color:#C1D72F"
+ id="stop38" />
+ <stop
+ offset="0.1394"
+ style="stop-color:#BCD631"
+ id="stop40" />
+ <stop
+ offset="0.5859"
+ style="stop-color:#AFD136"
+ id="stop42" />
+ <stop
+ offset="1"
+ style="stop-color:#ABD037"
+ id="stop44" />
+ <a:midPointStop
+ offset="0.0123"
+ style="stop-color:#C1D72F" />
+ <a:midPointStop
+ offset="0.3086"
+ style="stop-color:#C1D72F" />
+ <a:midPointStop
+ offset="1"
+ style="stop-color:#ABD037" />
+ </linearGradient>
+ <path
+ d="M184.562,236.826c0,5.965,4.835,10.8,10.8,10.8h59.663c5.964,0,10.8-4.835,10.8-10.8v-73.527 c0-5.964-4.835-10.8-10.8-10.8h-59.663c-5.964,0-10.8,4.835-10.8,10.8V236.826z"
+ id="path46"
+ fill="url(#SVGID_2_)" />
+ </g>
+ <defs
+ id="defs48">
+ <filter
+ id="Adobe_OpacityMaskFilter_1_"
+ filterUnits="userSpaceOnUse"
+ x="184.562"
+ y="152.499"
+ width="81.263"
+ height="95.127">
+
+ <feColorMatrix
+ type="matrix"
+ values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
+ color-interpolation-filters="sRGB"
+ result="source"
+ id="feColorMatrix51" />
+ </filter>
+ </defs>
+ <mask
+ maskUnits="userSpaceOnUse"
+ x="184.562"
+ y="152.499"
+ width="81.263"
+ height="95.127"
+ id="SVGID_3_">
+ <g
+ filter="url(#Adobe_OpacityMaskFilter_1_)"
+ id="g54">
+
+ <image
+ overflow="visible"
+ width="356"
+ height="414"
+ xlink:href="data:image/jpeg;base64,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 zFhO7bfhcyUttpzKJlCjZiwndtvwuZKW205lEyhRsxYTu234XMlLbacyiZQo2YsJ3bb8LmSlttOZ RMoUbMWE7tt+FzJS22nMomUKNmLCd22/C5kpbbTmUTKFGzFhO7bfhcyUttpzKJlCjZiwndtvwuZK W205lEyhRsxYTu234XMlLbacyiZQo2YsJ3bb8LmSlttOZRMoUbMWE7tt+FzJS22nMomUKNmLCd22 /C5kpbbTmUTKFGzFhO7bfhcyUttpzKJlCjZiwndtvwuZKW205lEyhRsxYTu234XMlLbacyiZQo2Y sJ3bb8LmSlttOZRMoUbMWE7tt+FzJS22nMomUKNmLCd22/C5kpbbTmUTKFGzFhO7bfhcyUttpzKJ lCjZiwndtvwuZKW205lEyhRsxYTu234XMlLbacyiZQo2YsJ3bb8LmSlttOZRMoUbMWE7tt+FzJS2 2nMomUKNmLCd22/C5kpbbTmUTKFGzFhO7bfhcyUttpzCZQo38SwndtvwuZKW20dTVfvQQ+cmUXiI xFhxUsJ3bb8LmSlttGVPp8/C/go1fkTjky8RyqsB9T6KvD8CwndtvwuZKW23BKNR3wqftX+qCcRO JwyLiLxF4i0Ka8V/cuFhO7bfhcyUttuCKi/CpBRtOs74cnBF/qJ+oiRIkSJEiRIkSJEiRIkSJEiR IkSJEiRIkSJEiRIkReIv6hadFfl31d9EFc5flViuFhO7bfhcyUttv2ERrvzNT/VT4qIrf+TqIdVD qodVDqpqdVNTqpqdVNTqpqdVNTqpqdVNTqpqdVNTqpqdVNTqpqdVNTqpqdVNTqpqdVNTqpqdVNTq pqdVNTqpqdVNTqpqdVDqodVDqC/xorl0F/O74av+qfYsJ3bb8LmSlttyywndtvzD/9k="
+ transform="matrix(0.24 0 0 0.24 182.5615 150.499)"
+ id="image56">
+ </image>
+ </g>
+ </mask>
+ <g
+ opacity="0.35"
+ mask="url(#SVGID_3_)"
+ a:adobe-opacity-share="1"
+ id="g58">
+ <path
+ a:adobe-opacity-share="0"
+ d="M184.562,236.826c0,5.965,4.835,10.8,10.8,10.8h59.663 c5.964,0,10.8-4.835,10.8-10.8v-73.527c0-5.964-4.835-10.8-10.8-10.8h-59.663c-5.964,0-10.8,4.835-10.8,10.8V236.826z"
+ id="path60"
+ fill="#1D2915" />
+ </g>
+</g>
+<linearGradient
+ id="SVGID_4_"
+ gradientUnits="userSpaceOnUse"
+ x1="226.1924"
+ y1="159.7139"
+ x2="226.1924"
+ y2="200">
+ <stop
+ offset="0.0123"
+ style="stop-color:#FFFFFF"
+ id="stop63" />
+ <stop
+ offset="0.3788"
+ style="stop-color:#F8FBF3"
+ id="stop65" />
+ <stop
+ offset="1"
+ style="stop-color:#F2F7E8"
+ id="stop67" />
+ <a:midPointStop
+ offset="0.0123"
+ style="stop-color:#FFFFFF" />
+ <a:midPointStop
+ offset="0.4383"
+ style="stop-color:#FFFFFF" />
+ <a:midPointStop
+ offset="1"
+ style="stop-color:#F2F7E8" />
+</linearGradient>
+<polygon
+ fill="url(#SVGID_4_)"
+ points="221.189,159.714 214.142,180.951 224.048,180.951 214.142,200 238.243,173.61 227.655,173.61 236.978,159.714 "
+ id="polygon69" />
+<g
+ id="g71">
+ <g
+ id="g73">
+ <g
+ id="g75">
+
+ <image
+ overflow="visible"
+ opacity="0.75"
+ a:adobe-blending-mode="multiply"
+ a:adobe-opacity-share="1"
+ width="392"
+ height="242"
+ xlink:href="data:image/png;base64,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 SUVORK5CYII="
+ transform="matrix(0.24 0 0 0.24 179.2061 198.1514)"
+ id="image77">
+ </image>
+ <g
+ id="g79">
+
+ <radialGradient
+ id="SVGID_5_"
+ cx="225.1929"
+ cy="226.1387"
+ r="30.8299"
+ gradientTransform="matrix(1 0 0 0.75 0 56.5347)"
+ gradientUnits="userSpaceOnUse">
+ <stop
+ offset="0.0123"
+ style="stop-color:#FFFFFF"
+ id="stop82" />
+ <stop
+ offset="0.4828"
+ style="stop-color:#FDFEFB"
+ id="stop84" />
+ <stop
+ offset="0.7611"
+ style="stop-color:#F8FBF3"
+ id="stop86" />
+ <stop
+ offset="0.989"
+ style="stop-color:#F2F8E8"
+ id="stop88" />
+ <stop
+ offset="1"
+ style="stop-color:#F2F7E8"
+ id="stop90" />
+ <a:midPointStop
+ offset="0.0123"
+ style="stop-color:#FFFFFF" />
+ <a:midPointStop
+ offset="0.8025"
+ style="stop-color:#FFFFFF" />
+ <a:midPointStop
+ offset="1"
+ style="stop-color:#F2F7E8" />
+ </radialGradient>
+ <path
+ fill="url(#SVGID_5_)"
+ d="M186.706,235.825c0,5.965,4.835,10.801,10.799,10.801h55.374c5.965,0,10.801-4.836,10.801-10.801 v-19.373c0-5.965-4.836-10.801-10.801-10.801h-55.374c-5.964,0-10.799,4.836-10.799,10.801V235.825z"
+ id="path92" />
+ <path
+ fill="none"
+ stroke="#EDF5E5"
+ stroke-width="5"
+ stroke-miterlimit="10"
+ d="M186.706,235.825 c0,5.965,4.835,10.801,10.799,10.801h55.374c5.965,0,10.801-4.836,10.801-10.801v-19.373c0-5.965-4.836-10.801-10.801-10.801 h-55.374c-5.964,0-10.799,4.836-10.799,10.801V235.825z"
+ id="path94" />
+ </g>
+ </g>
+ <path
+ opacity="0.74"
+ fill="#FFFFFF"
+ a:adobe-blending-mode="lighten"
+ d="M263.623,229.595c0.037-0.364,0.057-0.734,0.057-1.107 v-13.375c0-5.965-4.836-10.799-10.801-10.799h-55.374c-5.964,0-10.799,4.834-10.799,10.799v7.324 c7.545-1.012,15.699-1.566,24.213-1.566C231.959,220.87,250.812,224.252,263.623,229.595z"
+ id="path96" />
+ <linearGradient
+ id="SVGID_6_"
+ gradientUnits="userSpaceOnUse"
+ x1="225.1929"
+ y1="204.3135"
+ x2="225.1929"
+ y2="246.626">
+ <stop
+ offset="0.0123"
+ style="stop-color:#FFFFFF;stop-opacity:0"
+ id="stop99" />
+ <stop
+ offset="0.0141"
+ style="stop-color:#FDFDFC;stop-opacity:2.231669e-04"
+ id="stop101" />
+ <stop
+ offset="0.1344"
+ style="stop-color:#BEBEAF;stop-opacity:0.0148"
+ id="stop103" />
+ <stop
+ offset="0.2565"
+ style="stop-color:#94957C;stop-opacity:0.0297"
+ id="stop105" />
+ <stop
+ offset="0.3796"
+ style="stop-color:#747759;stop-opacity:0.0446"
+ id="stop107" />
+ <stop
+ offset="0.5029"
+ style="stop-color:#5D633F;stop-opacity:0.0596"
+ id="stop109" />
+ <stop
+ offset="0.6263"
+ style="stop-color:#4D552E;stop-opacity:0.0746"
+ id="stop111" />
+ <stop
+ offset="0.75"
+ style="stop-color:#414B23;stop-opacity:0.0896"
+ id="stop113" />
+ <stop
+ offset="0.8742"
+ style="stop-color:#3B461E;stop-opacity:0.1047"
+ id="stop115" />
+ <stop
+ offset="1"
+ style="stop-color:#38441C;stop-opacity:0.12"
+ id="stop117" />
+ <a:midPointStop
+ offset="0.0123"
+ style="stop-color:#FFFFFF;stop-opacity:0" />
+ <a:midPointStop
+ offset="0.2901"
+ style="stop-color:#FFFFFF;stop-opacity:0" />
+ <a:midPointStop
+ offset="1"
+ style="stop-color:#38441C;stop-opacity:0.12" />
+ </linearGradient>
+ <path
+ fill="url(#SVGID_6_)"
+ a:adobe-blending-mode="darken"
+ d="M263.68,221.954v13.871c0,5.965-4.836,10.801-10.801,10.801 h-55.374c-5.964,0-10.799-4.836-10.799-10.801v-13.871l0.038-7.704c0,0,0.923-9.937,11.173-9.937h54.962 c0,0,10.063,0.328,10.801,10.799V221.954z"
+ id="path119" />
+ </g>
+ <g
+ id="g121">
+ <g
+ id="g123">
+
+ <image
+ overflow="visible"
+ opacity="0.25"
+ a:adobe-blending-mode="multiply"
+ a:adobe-opacity-share="1"
+ width="30"
+ height="30"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAhCAYAAABX5MJvAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAuJJREFUeNrsl9trE0EUxjO7m5vW tKFN1RqLCmqlIvjgkz5I/cOFIqLggw9KsRHxUo1IdEtactG9+A1+A8dxNrsxK/rgwI9lt5ueb875 ZuZspfJ//Bhqjvc0AfCIHClIQEzSMkUoBqyCJbAKWrxXQoBmBL6AQzChmGQREWbmNQY/DS6Aa6AL mtZvdcDPoEcOQEgxUV5mVMYzH5wCZ8FFcJ0CLoN1UHeIGII34AV4BvbBW4qbzsqKctzruq+ALXAL 3ABXwAafNyjS9sQ3cAwG4BXYA0/AU/AejLOE+I4MtME22AH3wE2wyedNivSFQT3eB/y79kwHnGE2 v4IjinCaNrBEtJiBu2SLs686VkRWGRt8/wTL5jFwxIxMbSGB+Ac1qtcluEMBbWslFDV7QBFdlmBE bwxZtthVDn1dpgF3WIIOhakF9iCf2ajQK32W5hcRJgvnmYHb9ECzQAnyhif8o7PxkWImsiQeRSyJ fWCjJAGy5G2usKtgzc6wx5dWxT6wYhm2jKNBm/UcV90m/aLsdLVoonX+QJV8RvmcXNflNVOOKktQ Fz4p+6AMrBg/GUeeFWUHd51HyuXevz7+GRELNSRzjMwYnmhI5Laa/gEBYxEjskVE7Ih67AeOi3ZE BYc55j+xxzjgpBMpImZL1mNDMuDxm5aYBT2x1+wx+vZJ6lt94kl2Ux1uWl4JWZhy9g/AQ/DOPjt8 q0ULuLebhiRYYO8wPUTIdm+X1zDrKE/FKjH95TL3eP83MiIF7FHAY2ZkYpfadxhoRE80WJ66EKIK BE9YAiPgPkW8dPUSFUfDGnMpHVmKvQJCEoofcsamBLs0fOgSUMnomo2QQ66UAbMTi4+hmOk2mGZW B39OE+rgj5iBcNb3h5qxk9boDb1SLrEh2c75+NlnCfT1A4OP8nZiVeAT0IhZY0Ni+gHP8oEpQ59Z HHP2uRtfkeUnxTj7AWHqMU0ZiRVX2ld5kZ4jnSewHN8FGACSOOKkAlOGAAAAAABJRU5ErkJggg=="
+ transform="matrix(0.24 0 0 0.24 199.0298 216.5547)"
+ id="image125">
+ </image>
+ <g
+ id="g127">
+
+ <radialGradient
+ id="SVGID_7_"
+ cx="202.6289"
+ cy="219.7041"
+ r="2.9995"
+ gradientTransform="matrix(1 0 0 0.75 0 54.926)"
+ gradientUnits="userSpaceOnUse">
+ <stop
+ offset="0.0123"
+ style="stop-color:#FFFFFF"
+ id="stop130" />
+ <stop
+ offset="0.4235"
+ style="stop-color:#FAFCF6"
+ id="stop132" />
+ <stop
+ offset="1"
+ style="stop-color:#F2F7E8"
+ id="stop134" />
+ <a:midPointStop
+ offset="0.0123"
+ style="stop-color:#FFFFFF" />
+ <a:midPointStop
+ offset="0.6235"
+ style="stop-color:#FFFFFF" />
+ <a:midPointStop
+ offset="1"
+ style="stop-color:#F2F7E8" />
+ </radialGradient>
+ <circle
+ fill="url(#SVGID_7_)"
+ cx="202.629"
+ cy="219.704"
+ r="2.999"
+ id="circle136" />
+ </g>
+ </g>
+ <g
+ id="g138">
+
+ <image
+ overflow="visible"
+ opacity="0.25"
+ a:adobe-blending-mode="multiply"
+ a:adobe-opacity-share="1"
+ width="30"
+ height="30"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAhCAYAAAC1ONkWAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAtFJREFUeNrsmP1LFEEYx292Ts3V 9ujFrCiwFyPShH4I+imoiPqbhYKIoKigN0W8SulNIrOU63S921u/A9+BYdm9mbndg4IGPiynuzOf eea52WeuVvvf/Joo8VwA6rxm+0lBD3R5TYctpu6XYBRE4DiYzMhpqRbYBDtgHyQ+gsIzQmMUOg3O gzkwzciZTUXqO1gCH8E3CsauERSOUiOgAc6AC2ABXAYXwZECsV/gPVgBb8AH8AVsg45NTjhKTYGr 4Aa4AmYZqYjLmpdj+4ySilwTLIOn4C34YZOTjlLXwH1wC8xzKSMureQym0g+O85ITxP1uU3hPS6r t5j63zFK3QM3uYwNQ0g45KUSPMSJNLjsLS71blHUZJ9Ox5lDSuo2k32SHYsBvslKcAIc5jJvUK7r I1ZntK6Du8yryBJhW9P73hi3jg2ym7ek0hKtO0z4E5xx2RYYOany7DPYyotaUPCwyoVL3KemKpLS TX+h5jhGI88jT0x9/U9yrzoHwoL7ykQtZN8LHGvURUxy05xhntWH8I7WOTzDsaSLmOAMQl6DIYgF mTGEi5iWEyWqD9dtpHCMoPaXtn9KrHSR59CsYxSJ6SKv1e9FW6L1MmM4iXWMIu8ri7u04mjF7HuJ Y3VcxLqciaqfVlnYJRWKJexzlWNs5r2SZMGMNOp1cRYc5atEVBCtPVazD8AzHzHdQUKZUyzywoIT kY9Uh9XrC4o1WTimPmI9ouv9iAXfIHKm1GtKvSyKlq2C1Una5sMTLBRHPN4MOvIxpV6BRfCEJU/s W8Ganf4xzoaCf5dGaS36JHnMKnWNUg/BY35uD1rza7ku5bY4658cMDHkEt6nUZP4TQG1dI/Ic/CJ /SVVHHglS2J94pnluXLecuB9x3Nlk5+3jUlV9hOBMAQjCpn1lMikgFrCdQrtGEKp62CDlCtaMLQc eNu+QmV/7XGp2cyN2rsdCDAAoyXZx8WJpTUAAAAASUVORK5CYII="
+ transform="matrix(0.24 0 0 0.24 213.9448 216.5547)"
+ id="image140">
+ </image>
+ <g
+ id="g142">
+
+ <radialGradient
+ id="SVGID_8_"
+ cx="217.5439"
+ cy="219.7041"
+ r="2.9995"
+ gradientTransform="matrix(1 0 0 0.75 0 54.926)"
+ gradientUnits="userSpaceOnUse">
+ <stop
+ offset="0.0123"
+ style="stop-color:#FFFFFF"
+ id="stop145" />
+ <stop
+ offset="0.4235"
+ style="stop-color:#FAFCF6"
+ id="stop147" />
+ <stop
+ offset="1"
+ style="stop-color:#F2F7E8"
+ id="stop149" />
+ <a:midPointStop
+ offset="0.0123"
+ style="stop-color:#FFFFFF" />
+ <a:midPointStop
+ offset="0.6235"
+ style="stop-color:#FFFFFF" />
+ <a:midPointStop
+ offset="1"
+ style="stop-color:#F2F7E8" />
+ </radialGradient>
+ <circle
+ fill="url(#SVGID_8_)"
+ cx="217.544"
+ cy="219.704"
+ r="2.999"
+ id="circle151" />
+ </g>
+ </g>
+ <g
+ id="g153">
+
+ <image
+ overflow="visible"
+ opacity="0.25"
+ a:adobe-blending-mode="multiply"
+ a:adobe-opacity-share="1"
+ width="30"
+ height="30"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAhCAYAAAC1ONkWAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAttJREFUeNrsmO9r00Acxptc1m6d Fn9M125sU4RVpyjiSwXB/9wXulciiE4dhpUMpwzFUa02XdP4HDwHR7hcLk0mCh58WOnI9548973k uTYa/0e54c15nQ8E8TJ1UpKQ2Z8QJgUtgDa4CC6AliZOiYrBCfgGfoLTsgK9OQStgE2wDa6DDv+v hhQwBAdgH0TgS1mBLsIEBV0F18BNcJvC1sCyQdgIHFHYG/AODMAxBSZVhQXgHJ15AO5T2Aa4TMHC 0GMJBXwFhxT2Erygkz/AtGhim1NS1A3wCDwGO+AKBS3QKS+nrlp6eQProKe5G4LvNucCS0+1uXQP wRNwj6JaFkH6SgitNxeJ0BwNueSzMsIC3ulduiX/roJmpp9cWkVQ1CrrqB17ws+TPGdM3y3Rftnk fTpVVlS2ZpN1+qy7znl8V2HSrUvcdbKnutryVRk+63RZd5vzBC7ClFvyMXALbHEDiJreNGpDbbH+ Wp5rJmEd7sQ+n13NCq8uU881WbfPeTquwtq0u1ezW1nXepyn7SJM9dgSCc4oPBTO4Rus9jKJwatZ lGeZy+rYXzH+GWEpXxEq5Kl8VecwBcnURVjMgCcZn5GwsTZH7CJMD3khL5zWLGzKuiHnGZpe5CZh I6bO9wx7v+bN7YYxY70j1o/yEoZpKSdMmntMoMq1tIYlVG7ts/4x50tddmXCEDfgxRETZ1JRWMI6 EesObGFRWCyfaa+oDl8jQd4DscApJUr21S54Dj7wu1JBUW2Ct1rybDDRntditUtPnWor8Aw8Zd2h rXdFQdGYd6WfbPTYnOeeEiSv/cTDyC5FvbL1luspSSXPFUYUmaHu8KS0yfjdMpySYp6QIop6TZdC njEnRTvdpVc8Lt0yBW4wS+04HHj3+Fg4pKARnUxdJnVNBL7hSNal4OxPBFLAZ/CRzumn8NR1wrKR xdfy1KLlwDvmw3RaRlDVX3s8h8dGWiUE/BZgAMf82R9IYLF+AAAAAElFTkSuQmCC"
+ transform="matrix(0.24 0 0 0.24 228.8599 216.5547)"
+ id="image155">
+ </image>
+ <g
+ id="g157">
+
+ <radialGradient
+ id="SVGID_9_"
+ cx="232.459"
+ cy="219.7041"
+ r="2.9995"
+ gradientTransform="matrix(1 0 0 0.75 0 54.926)"
+ gradientUnits="userSpaceOnUse">
+ <stop
+ offset="0.0123"
+ style="stop-color:#FFFFFF"
+ id="stop160" />
+ <stop
+ offset="0.4235"
+ style="stop-color:#FAFCF6"
+ id="stop162" />
+ <stop
+ offset="1"
+ style="stop-color:#F2F7E8"
+ id="stop164" />
+ <a:midPointStop
+ offset="0.0123"
+ style="stop-color:#FFFFFF" />
+ <a:midPointStop
+ offset="0.6235"
+ style="stop-color:#FFFFFF" />
+ <a:midPointStop
+ offset="1"
+ style="stop-color:#F2F7E8" />
+ </radialGradient>
+ <circle
+ fill="url(#SVGID_9_)"
+ cx="232.459"
+ cy="219.704"
+ r="2.999"
+ id="circle166" />
+ </g>
+ </g>
+ <g
+ id="g168">
+
+ <image
+ overflow="visible"
+ opacity="0.25"
+ a:adobe-blending-mode="multiply"
+ a:adobe-opacity-share="1"
+ width="30"
+ height="30"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAhCAYAAABX5MJvAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAt9JREFUeNrsl91rE0EUxTOzm69a 05YmVWsUFdRKRfDBJ32Q+ocLRUTBBx+UYiOitlqRaEos2UT3w3P1jIzrbHZNVuiDAz9CNru5Z+69 M3O2Uvk/fg414zOCDzSxRwJiEJGkTBGKAatgEayCFr8rS4AwAp/BIRhTTDyPCDPzGoOfAhfANdAF zdT/SMBPoEf2wYBiQldmVAEBHjgJzoCL4DoFXAZroO4QMQRvwAvwDOyCtxQ3SWdF5QiQui+DDXAL 3ABXwDqvNygy3RPfwBHog1dgBzwBT8E7ENhCvJwMrIBNsAXugZvgPK83KdKzGlTzu8/fpWc64DSz +RV8oYhfTetPEdFiBu6SDc6+6lgRWZNo8P4Flk0zcMiMSGkSP+MPalQvJbhDASuplVB0RfkU0WUJ RuyNIcsWucoh15bYgFssQYfC1Bz7kcdsVNgrByzNHyJMFs4xA7fZA80CJcgb2uofycYHihlrh4hF ax9YL0mAGT7LKivsKmjLpLXjplVrH1ie0ryzlkWa9SxXnWR5QTv6ocUmWuMDqlLu8Di5ruk1Vzmq LEGdD5QtQln7yI8YespZUXbwzBi6cgzGsRTx14ZkxvFbDJeI9Laa/AMBgRUjTIsI6Yh69ANH0xzR DMMc8x/pMcTwjNIiIlqyHg1Jn8dvUmIWZGKv6THk/Jh4GWqFE3RTHW5auoQsTDj7B+Ah2JOzI8vU RNxQ2pYh8efYO4yHGNDubfNzkHWUJ9YqMf5yiZ7AmyEjtoAdCnjMjIj5TbycBhqxJxosT90SogoE j1kCI+A+Rbw0XmKaxzQlCXjz2GpOXUBITPFDztiUYJsNPzAC8kQklpBDrpQ+sxNZL0MR020wZlaC P2cTSvBHzMAg/f6hCu6qNfaGrJRLNCSbOS8/uyyBfL5n8JFrJy7a7Solpk1DYrynTvWBKcMBsxhw 9nEZL8S2GNtzuJo6YFOG1oor7a28iOdI8gLb47sAAwCDFN6m03jgxgAAAABJRU5ErkJggg=="
+ transform="matrix(0.24 0 0 0.24 243.7749 216.5547)"
+ id="image170">
+ </image>
+ <g
+ id="g172">
+
+ <radialGradient
+ id="SVGID_10_"
+ cx="247.374"
+ cy="219.7041"
+ r="2.9995"
+ gradientTransform="matrix(1 0 0 0.75 0 54.926)"
+ gradientUnits="userSpaceOnUse">
+ <stop
+ offset="0.0123"
+ style="stop-color:#FFFFFF"
+ id="stop175" />
+ <stop
+ offset="0.4235"
+ style="stop-color:#FAFCF6"
+ id="stop177" />
+ <stop
+ offset="1"
+ style="stop-color:#F2F7E8"
+ id="stop179" />
+ <a:midPointStop
+ offset="0.0123"
+ style="stop-color:#FFFFFF" />
+ <a:midPointStop
+ offset="0.6235"
+ style="stop-color:#FFFFFF" />
+ <a:midPointStop
+ offset="1"
+ style="stop-color:#F2F7E8" />
+ </radialGradient>
+ <circle
+ fill="url(#SVGID_10_)"
+ cx="247.374"
+ cy="219.704"
+ r="2.999"
+ id="circle181" />
+ </g>
+ </g>
+ <g
+ id="g183">
+
+ <image
+ overflow="visible"
+ opacity="0.25"
+ a:adobe-blending-mode="multiply"
+ a:adobe-opacity-share="1"
+ width="30"
+ height="30"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAhCAYAAABX5MJvAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAn9JREFUeNrsl+lrE0EYxvdKjSZW YxEPxBsVrNdHQTxA/KMFBRGPDwoVVIpoq3jUeJUG25qk2fVZ+A28WTabxG4lHzrwgxw78z7zzMw7 73reBDR/jOcCUREhn21LRCy6osfn0kT4BN0h9oiDoiGmTN8Efoum+CHWEBRvRoSbeZXgR8QZcVEc F7syfTcI/kq8Fgviu1jlv3hcET627xXHxFlxWZwXJ8RMxgmPIKkTn8UbMSdeinfiq1hnmUYS4QTs F5fENQSc4rfUgahgT7TFivgk5sVj8VQs4kqukGiAgCviDiKOijr/BUOWL7t/9uGaVyTEighYggsI uI79NQYfdY9FPF8x/WL2xiJLk9hOoelcJehtcYslqI8hYNDGrvP9G5t1PbtRQ+NIg/W/KWaxNNpk DnK5JZ35TzbuCq70ibAu3BBXxWHW1i8hGbpc0+akNLNuBDAtTopz4kBJAuxEC8cPzIlI88BpOoQl Xw1TuDtLsqvZkxbwUA2FjZxEVJYbdROj4mWOpVuzCMoWYGPlxggm4SrfFmFFJOTzDUi2KFY8KIYT sUoSWRadLRDSM0XPMgVPn4guOT0tSN6KVtHd/w8tYWJfqDHeM+m+jBkTeIEaoEmKTUp0oXD80Kjt kU4PkVSqJWTOhIDpFf5APBFLOOPliYhxZgYR00MKmVEEuKWeQ8Q8ruRe5Xb3po7s5CqvDSjnxhVw XzzjFu3k5XTbuuziNYLvZolCk+KHBU8n8QcBL8Rd8VB8yCto8kTEDNBCTIdBg4wQvyD4L6rsdOb3 xKNhhW44IKm4wZaghSAnoIdrHWhz/m3wlOfiI86OXPJPzMvPxLwG/tcX4u3m2l8BBgBQ/dU5d1Za tAAAAABJRU5ErkJggg=="
+ transform="matrix(0.24 0 0 0.24 199.0298 230.2217)"
+ id="image185">
+ </image>
+ <g
+ id="g187">
+
+ <radialGradient
+ id="SVGID_11_"
+ cx="202.6289"
+ cy="233.3711"
+ r="2.999"
+ gradientTransform="matrix(1 0 0 0.75 0 58.3428)"
+ gradientUnits="userSpaceOnUse">
+ <stop
+ offset="0.0123"
+ style="stop-color:#FFFFFF"
+ id="stop190" />
+ <stop
+ offset="0.4235"
+ style="stop-color:#FAFCF6"
+ id="stop192" />
+ <stop
+ offset="1"
+ style="stop-color:#F2F7E8"
+ id="stop194" />
+ <a:midPointStop
+ offset="0.0123"
+ style="stop-color:#FFFFFF" />
+ <a:midPointStop
+ offset="0.6235"
+ style="stop-color:#FFFFFF" />
+ <a:midPointStop
+ offset="1"
+ style="stop-color:#F2F7E8" />
+ </radialGradient>
+ <circle
+ fill="url(#SVGID_11_)"
+ cx="202.629"
+ cy="233.37"
+ r="2.999"
+ id="circle196" />
+ </g>
+ </g>
+ <g
+ id="g198">
+
+ <image
+ overflow="visible"
+ opacity="0.25"
+ a:adobe-blending-mode="multiply"
+ a:adobe-opacity-share="1"
+ width="30"
+ height="30"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAhCAYAAAC1ONkWAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmlJREFUeNrsmM9r1EAUx3cz2XW1 il2wLLagIh5aeilUBC967EX/XA/1It5aUaiC2JNY/EWp0lZkG3c3id+Bz0AIaXayibKHDnzIJfPm s29eZl/Sas3paNeYZ0SHaz5OKmIx5pr+azF7fyh6oi+WxXURZGJZiUScim/iWERiUkWwXTFDlxG6 I1bFBnKd3P1jpPbEvviE4JlvBtueUl1xQ9wVa2ITsdtkzOTmxGTsALE34oP4KH6I0TS5tofUJXFT PBCPxToZ67OlZTUWkSmbsffipXglvos/ZXLGI1N2qx6Kp+KRuFcgVYRh/oJYEgNxle08EUNqsbJY SDAnZa8r1FlYoQzc02sFF5n/W/wskzMlAReopydkaoVtDWocL1bqGtv8Bblx0YSgJFt9xNbJXLfG uZctjQEx11gj9M2YDXCFiVviflmAGeQCYtlt/MxTOvbJmEFkFZqSyu9GNr7xEXNP4gbnVK/mFhZl rUdsd0B3fcQCDs3lcw7PJobJrRH4iE37g24qa6VrBK05HRdiTYjVbvI8xtQ1isSyTd4pE5secW6N xEdslGnyDmhd0oazFRF7j7VGPmIxPdQ+HNMWNzUmBfFj3+7C1YDtMG7RT3UaONMS+jErtC1e05tV EksyPdkAyToHbsqW2e51R7ygs42qNooJaU/oPF2TF8wgl2SkdsVz8Y5sJVXFUvryX6TfNXld5HwE U37cWUbqGdfDsto1njVxQjscQ8jc8+SckO25jqipHTK1i+Ro1peR7FM6pKH7StAhC7uam7CQI+J+ J7RNTb0lU7Vf3+b6hXeuPxH8948qF6Pq+CvAAGGezDColMK7AAAAAElFTkSuQmCC"
+ transform="matrix(0.24 0 0 0.24 213.9448 230.2217)"
+ id="image200">
+ </image>
+ <g
+ id="g202">
+
+ <radialGradient
+ id="SVGID_12_"
+ cx="217.5439"
+ cy="233.3711"
+ r="2.999"
+ gradientTransform="matrix(1 0 0 0.75 0 58.3428)"
+ gradientUnits="userSpaceOnUse">
+ <stop
+ offset="0.0123"
+ style="stop-color:#FFFFFF"
+ id="stop205" />
+ <stop
+ offset="0.4235"
+ style="stop-color:#FAFCF6"
+ id="stop207" />
+ <stop
+ offset="1"
+ style="stop-color:#F2F7E8"
+ id="stop209" />
+ <a:midPointStop
+ offset="0.0123"
+ style="stop-color:#FFFFFF" />
+ <a:midPointStop
+ offset="0.6235"
+ style="stop-color:#FFFFFF" />
+ <a:midPointStop
+ offset="1"
+ style="stop-color:#F2F7E8" />
+ </radialGradient>
+ <circle
+ fill="url(#SVGID_12_)"
+ cx="217.544"
+ cy="233.37"
+ r="2.999"
+ id="circle211" />
+ </g>
+ </g>
+ <g
+ id="g213">
+
+ <image
+ overflow="visible"
+ opacity="0.25"
+ a:adobe-blending-mode="multiply"
+ a:adobe-opacity-share="1"
+ width="30"
+ height="30"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAhCAYAAAC1ONkWAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAlRJREFUeNrsmM9LG0EUx5PdjTTW otDYWmoOQm2M/YGXnrxI/3IvnnoRW7TagocotqjQtLYp5pffgc/CECc7u+uGpuDAB2Y32Tff9+bN 7Jstlaa0lXM+E4gIAsd/BqIHpj+cpLBY0Ix4JBbhwYg4I+SvOIdf4jqrwHIOQUvihXgtVsWCQ9gP 8UV8El/Ft6wCyyl+DxH0XLwUb8UbxD0V1RE7ZtCO+I6oj2JPHIlTBPZ94sqe3yIi0hCb4h1Reibm iOK4HDPRuRJnRO+D2BGHRLSXJC7yRGpeNMV7sYXAeQSFCU6FRHIGB56IGveMI/uIGxu5JGFzTN0W wpqIijIsmpDFYZ55hah4xRpxP7MIM4NWWHEbTGGDKY1ybi8VKyVM/l2wYjtM+S1xrvww9x6KulgX K0QqvOOeGafGCnbrjOPScOtmnPA1Hm7Sr+TcjF2Rq2F3nb4zNVzCZsWyWMOr2XFe5WgB9urYX+ba KywkF1bJh8d4WWSrYLdhbdBhmohVrddNtYApLOUZI0jxgp6EMO8YQWlK272wuwobFlHkeVqqMVzC OlaR15mQMO8Yo8L6VpFnypNL0S1YWBe7h4wTVxneiP0RJ+KzaHE9KEjUAHst7J9wnWoqe7z9TVly QL9bwJQOsXOB3X36vbTVhfHqN16Zh49F2xXujK2PnWPsthhnkLYeiz0ziblrVZ55CkV7Ftrk1Q52 z5NmIkowdsUBIrIqz7SltR2la0vUNhxhP3PNP7RCf4CouPIs4jDS9p2U/svj21QfeKf6E8E/+ahy 37K2GwEGAJb/2mQI89WQAAAAAElFTkSuQmCC"
+ transform="matrix(0.24 0 0 0.24 228.8599 230.2217)"
+ id="image215">
+ </image>
+ <g
+ id="g217">
+
+ <radialGradient
+ id="SVGID_13_"
+ cx="232.459"
+ cy="233.3711"
+ r="2.999"
+ gradientTransform="matrix(1 0 0 0.75 0 58.3428)"
+ gradientUnits="userSpaceOnUse">
+ <stop
+ offset="0.0123"
+ style="stop-color:#FFFFFF"
+ id="stop220" />
+ <stop
+ offset="0.4235"
+ style="stop-color:#FAFCF6"
+ id="stop222" />
+ <stop
+ offset="1"
+ style="stop-color:#F2F7E8"
+ id="stop224" />
+ <a:midPointStop
+ offset="0.0123"
+ style="stop-color:#FFFFFF" />
+ <a:midPointStop
+ offset="0.6235"
+ style="stop-color:#FFFFFF" />
+ <a:midPointStop
+ offset="1"
+ style="stop-color:#F2F7E8" />
+ </radialGradient>
+ <circle
+ fill="url(#SVGID_13_)"
+ cx="232.459"
+ cy="233.37"
+ r="2.999"
+ id="circle226" />
+ </g>
+ </g>
+ <g
+ id="g228">
+
+ <image
+ overflow="visible"
+ opacity="0.25"
+ a:adobe-blending-mode="multiply"
+ a:adobe-opacity-share="1"
+ width="30"
+ height="30"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAhCAYAAABX5MJvAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnpJREFUeNrsl2lrE1EUhmdLjSZ2 sYgL4lqsYN0+CuIC4o8WFERcPii0YEsQtYpL3cVgW7PMjO+F55ZhmMlMzATyoRceSDKZc957zplz zzjOBCz3P+/xRE34fE6uWESiJ0I+VybCxekeMSMOizkxlbATwx/xRXwXWwiKRhFhd17H+TFxVlwU J8W+lJ0+zlfFmngjvolNrkXDinAJ+6w4IRbFZXFenBLzqUg4ODGR+CheimXxQrwWn8U2aSolwgo4 KC6Jawg4w28mAsGAmuiI3+KDaInH4qlYJyo7QoISAq6IO4g4Lppc8wrSl66fA0TNSQvJE+GRggsI uE74GxgvW8gB/68l7ouojXVSE/s5N9dxelvcIgXNIQTkFXaT718pViMiyjIa8OiZ/N8US4Q0GLEf 2d5iauYHhWtqpu8PiMINcVUcJbduBY3R9poOT4rpJdteRi1Mi9PinDhUkQC7/Cz7aRH2iTB9YIEb /IqPiSmiu0Sza3gZf2qgcC6jEVUVjWbCR83LOR8CqFpAMu07PrxJOMp3ReSJiOnnfYjH5DdK+sgS sUkT+SW6YxASJoYe46OXFtGjp5uB5JVop8/+EVfMxj4xY7w1m/YywtRmGmqhtlNhNMIs+36O2pB2 fYSmUq+gc8ZsyBzhD8QTsWEikyciomjnETFdMMiUEWBTvYyIFlGJ/ILqNRHZy1HeyBnnhhVwXzzj FO06BSHuUcVbON9Piuy7hlvCudnEXwSsiLvioXhnB5oiEREG2ojpYtRLCXEHOP/JlG12fk88yhp0 /RJNxRrbgDaCrICQqHWhw/OfdG54Lt4T2dIj/8S8/EzMa+DYX4h3l13/BBgABM7SO70ZkkMAAAAA SUVORK5CYII="
+ transform="matrix(0.24 0 0 0.24 243.7749 230.2217)"
+ id="image230">
+ </image>
+ <g
+ id="g232">
+
+ <radialGradient
+ id="SVGID_14_"
+ cx="247.374"
+ cy="233.3711"
+ r="2.999"
+ gradientTransform="matrix(1 0 0 0.75 0 58.3428)"
+ gradientUnits="userSpaceOnUse">
+ <stop
+ offset="0.0123"
+ style="stop-color:#FFFFFF"
+ id="stop235" />
+ <stop
+ offset="0.4235"
+ style="stop-color:#FAFCF6"
+ id="stop237" />
+ <stop
+ offset="1"
+ style="stop-color:#F2F7E8"
+ id="stop239" />
+ <a:midPointStop
+ offset="0.0123"
+ style="stop-color:#FFFFFF" />
+ <a:midPointStop
+ offset="0.6235"
+ style="stop-color:#FFFFFF" />
+ <a:midPointStop
+ offset="1"
+ style="stop-color:#F2F7E8" />
+ </radialGradient>
+ <circle
+ fill="url(#SVGID_14_)"
+ cx="247.374"
+ cy="233.37"
+ r="2.999"
+ id="circle241" />
+ </g>
+ </g>
+ </g>
+</g>
+<path
+ d="m 529.664,248.155 h 18.498 l -2.809,18.064 h 5.59 37.586 l 2.6,-17.718 c 4.98,-1.091 9.133,-3.455 12.512,-6.693 3.084,4.075 8.566,7.37 18.252,7.37 6.338,0 12.775,-1.807 17.174,-3.687 4.254,2.399 9.463,3.687 15.459,3.687 3.088,0 6.236,-0.355 9.426,-1.023 h 67.135 l 3.354,-24.827 -5.445,-0.764 1.879,-13.356 c 0.371,-2.386 0.449,-4.66 0.449,-6.156 l -0.008,-0.375 c -0.457,-12.191 -8.139,-19.765 -20.045,-19.765 -2.404,0 -4.623,0.314 -6.676,0.852 h -34.189 l -0.035,0.244 c -2.527,-0.701 -5.41,-1.096 -8.686,-1.096 -3.801,0 -7.406,0.555 -10.76,1.598 l 0.105,-0.746 h -12.467 l 1.826,-12.951 H 613.08 l -1.846,7.658 c -1.373,5.704 -2.213,5.793 -4.453,6.03 l -4.508,0.477 c -3.049,-1.424 -6.357,-2.065 -9.602,-2.065 -2.135,0 -4.275,0.284 -6.416,0.852 h -19.291 c 0.502,-1.772 0.775,-3.674 0.775,-5.678 0,-9.601 -6.846,-16.305 -16.646,-16.305 -11.055,0 -18.775,7.721 -18.775,18.776 0,0.951 0.082,1.869 0.219,2.764 -2.135,-0.288 -4.277,-0.409 -5.553,-0.409 -2.053,0 -4.072,0.288 -6.045,0.852 h -31.342 c -2.74,-0.553 -5.641,-0.852 -8.537,-0.852 -7.138,0 -13.492,1.674 -18.808,4.723 l -3.451,-1.461 c -3.711,-1.571 -11.232,-3.262 -18.979,-3.262 -8.933,0 -16.383,2.56 -21.576,7.016 -3.265,-4.473 -8.523,-7.016 -15.228,-7.016 -4.822,0 -9.021,1.477 -12.572,3.44 -2.996,-2.204 -6.796,-3.44 -11.115,-3.44 -2.327,0 -4.48,0.315 -6.476,0.852 h -33.963 l -0.035,0.245 c -2.526,-0.702 -5.41,-1.097 -8.687,-1.097 -20.458,0 -35.307,16.031 -35.307,38.117 0,17.363 10.785,28.149 28.148,28.149 3.087,0 6.236,-0.356 9.426,-1.023 h 88.816 c 3.706,0.676 7.669,1.023 11.154,1.023 8.907,0 16.278,-2.375 21.51,-6.593 4.872,4.252 11.585,6.593 19.728,6.593 3.053,0 6.206,-0.368 9.286,-1.023 h 44.664 2.069 z"
+ id="path243"
+ inkscape:connector-curvature="0"
+ style="fill:#f5f5f5" />
+<g
+ id="g245"
+ transform="translate(0,16)">
+ <g
+ id="g247">
+ <path
+ d="m 340.308,218.463 c -5.538,2.556 -11.588,4.26 -17.638,4.26 -13.377,0 -18.148,-7.839 -18.148,-18.148 0,-17.893 11.418,-28.117 25.307,-28.117 9.372,0 13.973,4.26 13.973,11.247 0,12.184 -12.865,15.763 -26.157,17.126 0.255,4.346 2.045,8.35 8.435,8.35 3.068,0 7.243,-0.937 12.355,-3.067 l 1.873,8.349 z m -8.095,-29.567 c 0,-2.045 -1.448,-3.237 -4.09,-3.237 -4.771,0 -8.69,4.175 -9.969,10.906 3.664,-0.511 14.059,-2.3 14.059,-7.669 z"
+ id="path249"
+ inkscape:connector-curvature="0"
+ style="fill:#383838" />
+ <path
+ d="m 394.07,221.7 -0.171,-0.255 1.789,-10.055 2.642,-18.063 c 0.512,-3.749 0.341,-5.623 -1.96,-5.623 -2.642,0 -5.794,2.727 -9.372,5.879 l -2.727,19.512 c -0.171,1.363 -0.171,1.534 1.022,1.704 l 4.26,0.597 -0.852,6.305 h -18.404 l -0.171,-0.341 1.875,-10.82 2.471,-17.212 c 0.512,-3.237 0.682,-5.453 -1.789,-5.453 -3.238,0 -7.413,3.664 -9.714,5.709 l -2.642,19.512 c -0.17,1.363 -0.17,1.534 1.108,1.704 l 4.26,0.597 -0.852,6.305 h -23.347 l 0.853,-6.39 3.749,-0.512 c 1.107,-0.17 1.363,-0.426 1.533,-1.704 l 3.579,-25.987 c 0.17,-0.938 0,-1.534 -0.767,-1.789 l -4.176,-1.534 0.938,-6.476 h 16.871 l -0.938,6.987 0.256,0.085 c 4.43,-3.749 9.116,-7.924 15.592,-7.924 4.687,0 7.839,2.641 8.18,7.753 l 0.256,0.086 c 4.175,-3.664 9.202,-7.839 15.252,-7.839 6.22,0 8.775,3.152 8.946,9.202 0,1.618 -0.171,3.493 -0.426,5.538 l -3.067,21.897 c -0.171,1.363 -0.171,1.534 1.107,1.704 l 4.175,0.597 -0.852,6.305 H 394.07 z"
+ id="path251"
+ inkscape:connector-curvature="0"
+ style="fill:#383838" />
+ <path
+ d="m 443.995,190.771 -0.17,-4.431 c 0,-0.682 -0.085,-1.108 -1.022,-1.363 -1.022,-0.256 -2.642,-0.427 -4.771,-0.427 -3.579,0 -6.391,1.108 -6.391,4.09 0,2.727 2.982,3.749 6.731,5.027 6.05,2.045 13.888,4.431 13.888,13.463 0,11.076 -9.372,15.592 -20.193,15.592 -8.009,0 -14.91,-1.959 -16.273,-2.981 l 1.618,-12.355 8.691,0.512 0.255,4.941 c 0,0.597 0.171,1.108 0.938,1.363 1.278,0.427 3.238,0.768 6.05,0.768 4.687,0 7.327,-1.79 7.327,-4.687 0,-3.408 -3.152,-4.175 -8.009,-5.624 -6.135,-1.874 -12.78,-4.26 -12.78,-13.206 0,-10.48 9.116,-14.996 19.597,-14.996 6.646,0 12.866,1.533 15.081,2.471 l -1.704,12.354 -8.863,-0.511 z"
+ id="path253"
+ inkscape:connector-curvature="0"
+ style="fill:#383838" />
+ <path
+ d="m 489.748,218.548 c -4.175,2.386 -10.395,4.175 -16.444,4.175 -13.036,0 -18.575,-7.583 -18.575,-18.574 0,-18.83 11.588,-27.691 25.988,-27.691 6.475,0 11.843,1.874 14.229,3.578 l -1.874,13.377 -8.691,-0.426 -0.255,-5.794 c 0,-0.597 -0.086,-0.938 -0.597,-1.192 -1.022,-0.427 -2.557,-0.597 -4.175,-0.597 -5.624,0 -11.418,4.601 -11.418,17.382 0,7.839 3.493,10.395 8.436,10.395 4.346,0 8.436,-1.448 11.247,-2.556 l 2.129,7.923 z"
+ id="path255"
+ inkscape:connector-curvature="0"
+ style="fill:#383838" />
+ <path
+ d="m 491.364,221.7 0.853,-6.39 3.919,-0.512 c 1.193,-0.17 1.363,-0.426 1.534,-1.704 l 3.578,-25.987 c 0.086,-0.938 -0.085,-1.534 -0.852,-1.789 l -4.261,-1.534 0.938,-6.476 h 16.87 l -1.107,7.669 0.256,0.17 c 3.323,-4.771 8.095,-8.69 13.548,-8.69 1.874,0 5.112,0.341 6.561,0.767 l -2.13,15.507 -9.969,-0.341 -0.256,-4.431 c -0.086,-0.767 -0.256,-1.022 -0.938,-1.022 -1.619,0 -4.26,1.96 -6.646,4.431 l -2.981,21.643 c -0.171,1.363 -0.085,1.619 1.192,1.704 l 8.095,0.682 -0.938,6.305 h -27.266 z"
+ id="path257"
+ inkscape:connector-curvature="0"
+ style="fill:#383838" />
+ <path
+ d="m 536.094,221.7 -0.17,-0.426 2.045,-11.503 3.152,-22.749 c 0.17,-0.938 -0.086,-1.534 -0.853,-1.79 l -4.175,-1.448 0.852,-6.476 h 18.149 l -5.027,35.786 c -0.171,1.363 -0.085,1.534 1.192,1.704 l 4.09,0.597 -0.852,6.305 h -18.403 z m 5.879,-57.598 c 0,-5.453 3.238,-8.775 8.776,-8.775 4.175,0 6.646,2.215 6.646,6.305 0,5.368 -3.322,8.861 -8.861,8.861 -4.176,-0.001 -6.561,-2.387 -6.561,-6.391 z"
+ id="path259"
+ inkscape:connector-curvature="0"
+ style="fill:#383838" />
+ <path
+ d="m 556.796,239.764 -0.17,-0.341 2.471,-14.229 5.282,-38.087 c 0.171,-1.022 -0.085,-1.534 -0.767,-1.789 l -4.175,-1.534 0.938,-6.476 h 17.041 l -1.022,6.816 0.255,0.085 c 5.027,-4.686 10.311,-7.753 15.678,-7.753 7.328,0 12.44,4.686 12.44,17.041 0,11.758 -4.601,29.225 -20.449,29.225 -5.538,0 -8.605,-2.13 -11.759,-4.345 l -1.874,12.78 c -0.085,0.938 0.085,1.278 1.192,1.363 l 8.606,0.853 -0.938,6.39 h -22.749 z m 17.041,-30.247 c 2.13,1.789 4.942,3.322 8.095,3.322 6.901,0 9.458,-9.713 9.458,-17.211 0,-5.027 -1.193,-8.351 -4.431,-8.351 -3.408,0 -7.754,3.664 -10.821,6.391 l -2.301,15.849 z"
+ id="path261"
+ inkscape:connector-curvature="0"
+ style="fill:#383838" />
+ <path
+ d="m 635.777,219.4 c -3.749,1.789 -9.458,3.322 -14.229,3.322 -8.521,0 -12.099,-2.981 -12.099,-9.969 0,-1.107 0.085,-2.386 0.256,-3.749 l 3.066,-22.323 c 0.086,-0.512 0.086,-0.853 -0.511,-0.853 h -5.879 l 1.107,-7.839 c 7.242,-0.767 10.906,-4.431 13.122,-13.633 h 7.924 l -1.704,12.1 c -0.085,0.596 -0.085,0.852 0.597,0.852 h 11.758 l -1.193,8.521 h -12.439 l -2.812,20.364 c -0.171,1.107 -0.256,1.96 -0.256,2.727 0,2.982 1.278,4.26 4.942,4.26 2.385,0 4.771,-0.596 6.816,-1.363 l 1.534,7.583 z"
+ id="path263"
+ inkscape:connector-curvature="0"
+ style="fill:#383838" />
+ <path
+ d="m 671.817,218.463 c -5.538,2.556 -11.588,4.26 -17.638,4.26 -13.377,0 -18.148,-7.839 -18.148,-18.148 0,-17.893 11.418,-28.117 25.307,-28.117 9.372,0 13.973,4.26 13.973,11.247 0,12.184 -12.865,15.763 -26.157,17.126 0.255,4.346 2.045,8.35 8.435,8.35 3.068,0 7.243,-0.937 12.355,-3.067 l 1.873,8.349 z m -8.094,-29.567 c 0,-2.045 -1.448,-3.237 -4.09,-3.237 -4.771,0 -8.69,4.175 -9.969,10.906 3.664,-0.511 14.059,-2.3 14.059,-7.669 z"
+ id="path265"
+ inkscape:connector-curvature="0"
+ style="fill:#383838" />
+ <path
+ d="m 703.596,221.7 -0.17,-0.255 1.874,-10.396 2.471,-17.723 c 0.512,-3.578 0.341,-5.879 -2.215,-5.879 -3.664,0 -8.18,3.578 -11.077,6.135 l -2.641,19.512 c -0.171,1.363 -0.171,1.534 1.107,1.704 l 4.26,0.597 -0.852,6.305 h -23.347 l 0.853,-6.39 3.749,-0.512 c 1.107,-0.17 1.363,-0.426 1.533,-1.704 l 3.579,-25.987 c 0.17,-0.938 0,-1.534 -0.768,-1.789 l -4.175,-1.534 0.938,-6.476 h 16.87 l -0.937,6.987 0.255,0.085 c 4.771,-4.09 9.373,-7.924 16.02,-7.924 6.475,0 9.798,3.322 10.054,10.139 0,1.363 -0.085,3.067 -0.341,4.687 l -3.067,21.812 c -0.171,1.363 -0.171,1.534 1.022,1.704 l 4.26,0.597 L 722,221.7 h -18.404 z"
+ id="path267"
+ inkscape:connector-curvature="0"
+ style="fill:#383838" />
+ </g>
+ <g
+ id="g269">
+ <linearGradient
+ id="SVGID_15_"
+ gradientUnits="userSpaceOnUse"
+ x1="324.1611"
+ y1="239.7637"
+ x2="324.1611"
+ y2="155.3275">
+ <stop
+ offset="0"
+ style="stop-color:#000000"
+ id="stop272" />
+ <stop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0"
+ id="stop274" />
+ <a:midPointStop
+ offset="0"
+ style="stop-color:#000000" />
+ <a:midPointStop
+ offset="0.6933"
+ style="stop-color:#000000" />
+ <a:midPointStop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0" />
+ </linearGradient>
+ <path
+ d="m 340.308,218.463 c -5.538,2.556 -11.588,4.26 -17.638,4.26 -13.377,0 -18.148,-7.839 -18.148,-18.148 0,-17.893 11.418,-28.117 25.307,-28.117 9.372,0 13.973,4.26 13.973,11.247 0,12.184 -12.865,15.763 -26.157,17.126 0.255,4.346 2.045,8.35 8.435,8.35 3.068,0 7.243,-0.937 12.355,-3.067 l 1.873,8.349 z m -8.095,-29.567 c 0,-2.045 -1.448,-3.237 -4.09,-3.237 -4.771,0 -8.69,4.175 -9.969,10.906 3.664,-0.511 14.059,-2.3 14.059,-7.669 z"
+ id="path276"
+ style="fill:url(#SVGID_15_)"
+ inkscape:connector-curvature="0" />
+ <linearGradient
+ id="SVGID_16_"
+ gradientUnits="userSpaceOnUse"
+ x1="377.45459"
+ y1="239.7637"
+ x2="377.45459"
+ y2="155.3277">
+ <stop
+ offset="0"
+ style="stop-color:#000000"
+ id="stop279" />
+ <stop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0"
+ id="stop281" />
+ <a:midPointStop
+ offset="0"
+ style="stop-color:#000000" />
+ <a:midPointStop
+ offset="0.6933"
+ style="stop-color:#000000" />
+ <a:midPointStop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0" />
+ </linearGradient>
+ <path
+ d="m 394.07,221.7 -0.171,-0.255 1.789,-10.055 2.642,-18.063 c 0.512,-3.749 0.341,-5.623 -1.96,-5.623 -2.642,0 -5.794,2.727 -9.372,5.879 l -2.727,19.512 c -0.171,1.363 -0.171,1.534 1.022,1.704 l 4.26,0.597 -0.852,6.305 h -18.404 l -0.171,-0.341 1.875,-10.82 2.471,-17.212 c 0.512,-3.237 0.682,-5.453 -1.789,-5.453 -3.238,0 -7.413,3.664 -9.714,5.709 l -2.642,19.512 c -0.17,1.363 -0.17,1.534 1.108,1.704 l 4.26,0.597 -0.852,6.305 h -23.347 l 0.853,-6.39 3.749,-0.512 c 1.107,-0.17 1.363,-0.426 1.533,-1.704 l 3.579,-25.987 c 0.17,-0.938 0,-1.534 -0.767,-1.789 l -4.176,-1.534 0.938,-6.476 h 16.871 l -0.938,6.987 0.256,0.085 c 4.43,-3.749 9.116,-7.924 15.592,-7.924 4.687,0 7.839,2.641 8.18,7.753 l 0.256,0.086 c 4.175,-3.664 9.202,-7.839 15.252,-7.839 6.22,0 8.775,3.152 8.946,9.202 0,1.618 -0.171,3.493 -0.426,5.538 l -3.067,21.897 c -0.171,1.363 -0.171,1.534 1.107,1.704 l 4.175,0.597 -0.852,6.305 H 394.07 z"
+ id="path283"
+ style="fill:url(#SVGID_16_)"
+ inkscape:connector-curvature="0" />
+ <linearGradient
+ id="SVGID_17_"
+ gradientUnits="userSpaceOnUse"
+ x1="435.17719"
+ y1="239.7637"
+ x2="435.17719"
+ y2="155.3275">
+ <stop
+ offset="0"
+ style="stop-color:#000000"
+ id="stop286" />
+ <stop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0"
+ id="stop288" />
+ <a:midPointStop
+ offset="0"
+ style="stop-color:#000000" />
+ <a:midPointStop
+ offset="0.6933"
+ style="stop-color:#000000" />
+ <a:midPointStop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0" />
+ </linearGradient>
+ <path
+ d="m 443.995,190.771 -0.17,-4.431 c 0,-0.682 -0.085,-1.108 -1.022,-1.363 -1.022,-0.256 -2.642,-0.427 -4.771,-0.427 -3.579,0 -6.391,1.108 -6.391,4.09 0,2.727 2.982,3.749 6.731,5.027 6.05,2.045 13.888,4.431 13.888,13.463 0,11.076 -9.372,15.592 -20.193,15.592 -8.009,0 -14.91,-1.959 -16.273,-2.981 l 1.618,-12.355 8.691,0.512 0.255,4.941 c 0,0.597 0.171,1.108 0.938,1.363 1.278,0.427 3.238,0.768 6.05,0.768 4.687,0 7.327,-1.79 7.327,-4.687 0,-3.408 -3.152,-4.175 -8.009,-5.624 -6.135,-1.874 -12.78,-4.26 -12.78,-13.206 0,-10.48 9.116,-14.996 19.597,-14.996 6.646,0 12.866,1.533 15.081,2.471 l -1.704,12.354 -8.863,-0.511 z"
+ id="path290"
+ style="fill:url(#SVGID_17_)"
+ inkscape:connector-curvature="0" />
+ <linearGradient
+ id="SVGID_18_"
+ gradientUnits="userSpaceOnUse"
+ x1="474.83691"
+ y1="239.7637"
+ x2="474.83691"
+ y2="155.3275">
+ <stop
+ offset="0"
+ style="stop-color:#000000"
+ id="stop293" />
+ <stop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0"
+ id="stop295" />
+ <a:midPointStop
+ offset="0"
+ style="stop-color:#000000" />
+ <a:midPointStop
+ offset="0.6933"
+ style="stop-color:#000000" />
+ <a:midPointStop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0" />
+ </linearGradient>
+ <path
+ d="m 489.748,218.548 c -4.175,2.386 -10.395,4.175 -16.444,4.175 -13.036,0 -18.575,-7.583 -18.575,-18.574 0,-18.83 11.588,-27.691 25.988,-27.691 6.475,0 11.843,1.874 14.229,3.578 l -1.874,13.377 -8.691,-0.426 -0.255,-5.794 c 0,-0.597 -0.086,-0.938 -0.597,-1.192 -1.022,-0.427 -2.557,-0.597 -4.175,-0.597 -5.624,0 -11.418,4.601 -11.418,17.382 0,7.839 3.493,10.395 8.436,10.395 4.346,0 8.436,-1.448 11.247,-2.556 l 2.129,7.923 z"
+ id="path297"
+ style="fill:url(#SVGID_18_)"
+ inkscape:connector-curvature="0" />
+ <linearGradient
+ id="SVGID_19_"
+ gradientUnits="userSpaceOnUse"
+ x1="512.28223"
+ y1="239.7637"
+ x2="512.28223"
+ y2="155.3277">
+ <stop
+ offset="0"
+ style="stop-color:#000000"
+ id="stop300" />
+ <stop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0"
+ id="stop302" />
+ <a:midPointStop
+ offset="0"
+ style="stop-color:#000000" />
+ <a:midPointStop
+ offset="0.6933"
+ style="stop-color:#000000" />
+ <a:midPointStop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0" />
+ </linearGradient>
+ <path
+ d="m 491.364,221.7 0.853,-6.39 3.919,-0.512 c 1.193,-0.17 1.363,-0.426 1.534,-1.704 l 3.578,-25.987 c 0.086,-0.938 -0.085,-1.534 -0.852,-1.789 l -4.261,-1.534 0.938,-6.476 h 16.87 l -1.107,7.669 0.256,0.17 c 3.323,-4.771 8.095,-8.69 13.548,-8.69 1.874,0 5.112,0.341 6.561,0.767 l -2.13,15.507 -9.969,-0.341 -0.256,-4.431 c -0.086,-0.767 -0.256,-1.022 -0.938,-1.022 -1.619,0 -4.26,1.96 -6.646,4.431 l -2.981,21.643 c -0.171,1.363 -0.085,1.619 1.192,1.704 l 8.095,0.682 -0.938,6.305 h -27.266 z"
+ id="path304"
+ style="fill:url(#SVGID_19_)"
+ inkscape:connector-curvature="0" />
+ <linearGradient
+ id="SVGID_20_"
+ gradientUnits="userSpaceOnUse"
+ x1="546.65918"
+ y1="239.7637"
+ x2="546.65918"
+ y2="155.32719">
+ <stop
+ offset="0"
+ style="stop-color:#000000"
+ id="stop307" />
+ <stop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0"
+ id="stop309" />
+ <a:midPointStop
+ offset="0"
+ style="stop-color:#000000" />
+ <a:midPointStop
+ offset="0.6933"
+ style="stop-color:#000000" />
+ <a:midPointStop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0" />
+ </linearGradient>
+ <path
+ d="m 536.094,221.7 -0.17,-0.426 2.045,-11.503 3.152,-22.749 c 0.17,-0.938 -0.086,-1.534 -0.853,-1.79 l -4.175,-1.448 0.852,-6.476 h 18.149 l -5.027,35.786 c -0.171,1.363 -0.085,1.534 1.192,1.704 l 4.09,0.597 -0.852,6.305 h -18.403 z m 5.879,-57.598 c 0,-5.453 3.238,-8.775 8.776,-8.775 4.175,0 6.646,2.215 6.646,6.305 0,5.368 -3.322,8.861 -8.861,8.861 -4.176,-0.001 -6.561,-2.387 -6.561,-6.391 z"
+ id="path311"
+ style="fill:url(#SVGID_20_)"
+ inkscape:connector-curvature="0" />
+ <linearGradient
+ id="SVGID_21_"
+ gradientUnits="userSpaceOnUse"
+ x1="580.69629"
+ y1="239.7637"
+ x2="580.69629"
+ y2="155.32719">
+ <stop
+ offset="0"
+ style="stop-color:#000000"
+ id="stop314" />
+ <stop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0"
+ id="stop316" />
+ <a:midPointStop
+ offset="0"
+ style="stop-color:#000000" />
+ <a:midPointStop
+ offset="0.6933"
+ style="stop-color:#000000" />
+ <a:midPointStop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0" />
+ </linearGradient>
+ <path
+ d="m 556.796,239.764 -0.17,-0.341 2.471,-14.229 5.282,-38.087 c 0.171,-1.022 -0.085,-1.534 -0.767,-1.789 l -4.175,-1.534 0.938,-6.476 h 17.041 l -1.022,6.816 0.255,0.085 c 5.027,-4.686 10.311,-7.753 15.678,-7.753 7.328,0 12.44,4.686 12.44,17.041 0,11.758 -4.601,29.225 -20.449,29.225 -5.538,0 -8.605,-2.13 -11.759,-4.345 l -1.874,12.78 c -0.085,0.938 0.085,1.278 1.192,1.363 l 8.606,0.853 -0.938,6.39 h -22.749 z m 17.041,-30.247 c 2.13,1.789 4.942,3.322 8.095,3.322 6.901,0 9.458,-9.713 9.458,-17.211 0,-5.027 -1.193,-8.351 -4.431,-8.351 -3.408,0 -7.754,3.664 -10.821,6.391 l -2.301,15.849 z"
+ id="path318"
+ style="fill:url(#SVGID_21_)"
+ inkscape:connector-curvature="0" />
+ <linearGradient
+ id="SVGID_22_"
+ gradientUnits="userSpaceOnUse"
+ x1="622.7832"
+ y1="239.7637"
+ x2="622.7832"
+ y2="155.3268">
+ <stop
+ offset="0"
+ style="stop-color:#000000"
+ id="stop321" />
+ <stop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0"
+ id="stop323" />
+ <a:midPointStop
+ offset="0"
+ style="stop-color:#000000" />
+ <a:midPointStop
+ offset="0.6933"
+ style="stop-color:#000000" />
+ <a:midPointStop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0" />
+ </linearGradient>
+ <path
+ d="m 635.777,219.4 c -3.749,1.789 -9.458,3.322 -14.229,3.322 -8.521,0 -12.099,-2.981 -12.099,-9.969 0,-1.107 0.085,-2.386 0.256,-3.749 l 3.066,-22.323 c 0.086,-0.512 0.086,-0.853 -0.511,-0.853 h -5.879 l 1.107,-7.839 c 7.242,-0.767 10.906,-4.431 13.122,-13.633 h 7.924 l -1.704,12.1 c -0.085,0.596 -0.085,0.852 0.597,0.852 h 11.758 l -1.193,8.521 h -12.439 l -2.812,20.364 c -0.171,1.107 -0.256,1.96 -0.256,2.727 0,2.982 1.278,4.26 4.942,4.26 2.385,0 4.771,-0.596 6.816,-1.363 l 1.534,7.583 z"
+ id="path325"
+ style="fill:url(#SVGID_22_)"
+ inkscape:connector-curvature="0" />
+ <linearGradient
+ id="SVGID_23_"
+ gradientUnits="userSpaceOnUse"
+ x1="655.6709"
+ y1="239.7637"
+ x2="655.6709"
+ y2="155.3275">
+ <stop
+ offset="0"
+ style="stop-color:#000000"
+ id="stop328" />
+ <stop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0"
+ id="stop330" />
+ <a:midPointStop
+ offset="0"
+ style="stop-color:#000000" />
+ <a:midPointStop
+ offset="0.6933"
+ style="stop-color:#000000" />
+ <a:midPointStop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0" />
+ </linearGradient>
+ <path
+ d="m 671.817,218.463 c -5.538,2.556 -11.588,4.26 -17.638,4.26 -13.377,0 -18.148,-7.839 -18.148,-18.148 0,-17.893 11.418,-28.117 25.307,-28.117 9.372,0 13.973,4.26 13.973,11.247 0,12.184 -12.865,15.763 -26.157,17.126 0.255,4.346 2.045,8.35 8.435,8.35 3.068,0 7.243,-0.937 12.355,-3.067 l 1.873,8.349 z m -8.094,-29.567 c 0,-2.045 -1.448,-3.237 -4.09,-3.237 -4.771,0 -8.69,4.175 -9.969,10.906 3.664,-0.511 14.059,-2.3 14.059,-7.669 z"
+ id="path332"
+ style="fill:url(#SVGID_23_)"
+ inkscape:connector-curvature="0" />
+ <linearGradient
+ id="SVGID_24_"
+ gradientUnits="userSpaceOnUse"
+ x1="697.92969"
+ y1="239.7637"
+ x2="697.92969"
+ y2="155.3277">
+ <stop
+ offset="0"
+ style="stop-color:#000000"
+ id="stop335" />
+ <stop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0"
+ id="stop337" />
+ <a:midPointStop
+ offset="0"
+ style="stop-color:#000000" />
+ <a:midPointStop
+ offset="0.6933"
+ style="stop-color:#000000" />
+ <a:midPointStop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0" />
+ </linearGradient>
+ <path
+ d="m 703.596,221.7 -0.17,-0.255 1.874,-10.396 2.471,-17.723 c 0.512,-3.578 0.341,-5.879 -2.215,-5.879 -3.664,0 -8.18,3.578 -11.077,6.135 l -2.641,19.512 c -0.171,1.363 -0.171,1.534 1.107,1.704 l 4.26,0.597 -0.852,6.305 h -23.347 l 0.853,-6.39 3.749,-0.512 c 1.107,-0.17 1.363,-0.426 1.533,-1.704 l 3.579,-25.987 c 0.17,-0.938 0,-1.534 -0.768,-1.789 l -4.175,-1.534 0.938,-6.476 h 16.87 l -0.937,6.987 0.255,0.085 c 4.771,-4.09 9.373,-7.924 16.02,-7.924 6.475,0 9.798,3.322 10.054,10.139 0,1.363 -0.085,3.067 -0.341,4.687 l -3.067,21.812 c -0.171,1.363 -0.171,1.534 1.022,1.704 l 4.26,0.597 L 722,221.7 h -18.404 z"
+ id="path339"
+ style="fill:url(#SVGID_24_)"
+ inkscape:connector-curvature="0" />
+ </g>
+</g>
+<g
+ id="g4141"
+ transform="matrix(0.81856441,0,0,0.81856441,79.234731,-94.128741)">
+ <g
+ id="g4143">
+
+
+
+
+
+
+
+
+
+
+ </g>
+ <g
+ id="g4165">
+ <linearGradient
+ y2="155.3275"
+ x2="324.1611"
+ y1="239.7637"
+ x1="324.1611"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4167">
+ <stop
+ id="stop4169"
+ style="stop-color:#000000"
+ offset="0" />
+ <stop
+ id="stop4171"
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" />
+ <a:midPointStop
+ style="stop-color:#000000"
+ offset="0" />
+ <a:midPointStop
+ style="stop-color:#000000"
+ offset="0.6933" />
+ <a:midPointStop
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" />
+ </linearGradient>
+
+ <linearGradient
+ y2="155.3277"
+ x2="377.45459"
+ y1="239.7637"
+ x1="377.45459"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4175">
+ <stop
+ id="stop4177"
+ style="stop-color:#000000"
+ offset="0" />
+ <stop
+ id="stop4179"
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" />
+ <a:midPointStop
+ style="stop-color:#000000"
+ offset="0" />
+ <a:midPointStop
+ style="stop-color:#000000"
+ offset="0.6933" />
+ <a:midPointStop
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" />
+ </linearGradient>
+
+ <linearGradient
+ y2="155.3275"
+ x2="435.17719"
+ y1="239.7637"
+ x1="435.17719"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4183">
+ <stop
+ id="stop4185"
+ style="stop-color:#000000"
+ offset="0" />
+ <stop
+ id="stop4187"
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" />
+ <a:midPointStop
+ style="stop-color:#000000"
+ offset="0" />
+ <a:midPointStop
+ style="stop-color:#000000"
+ offset="0.6933" />
+ <a:midPointStop
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" />
+ </linearGradient>
+
+ <linearGradient
+ y2="155.3275"
+ x2="474.83691"
+ y1="239.7637"
+ x1="474.83691"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4191">
+ <stop
+ id="stop4193"
+ style="stop-color:#000000"
+ offset="0" />
+ <stop
+ id="stop4195"
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" />
+ <a:midPointStop
+ style="stop-color:#000000"
+ offset="0" />
+ <a:midPointStop
+ style="stop-color:#000000"
+ offset="0.6933" />
+ <a:midPointStop
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" />
+ </linearGradient>
+
+ <linearGradient
+ y2="155.3277"
+ x2="512.28223"
+ y1="239.7637"
+ x1="512.28223"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4199">
+ <stop
+ id="stop4201"
+ style="stop-color:#000000"
+ offset="0" />
+ <stop
+ id="stop4203"
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" />
+ <a:midPointStop
+ style="stop-color:#000000"
+ offset="0" />
+ <a:midPointStop
+ style="stop-color:#000000"
+ offset="0.6933" />
+ <a:midPointStop
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" />
+ </linearGradient>
+
+ <linearGradient
+ y2="155.32719"
+ x2="546.65918"
+ y1="239.7637"
+ x1="546.65918"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4207">
+ <stop
+ id="stop4209"
+ style="stop-color:#000000"
+ offset="0" />
+ <stop
+ id="stop4211"
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" />
+ <a:midPointStop
+ style="stop-color:#000000"
+ offset="0" />
+ <a:midPointStop
+ style="stop-color:#000000"
+ offset="0.6933" />
+ <a:midPointStop
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" />
+ </linearGradient>
+
+ <linearGradient
+ y2="155.32719"
+ x2="580.69629"
+ y1="239.7637"
+ x1="580.69629"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4215">
+ <stop
+ id="stop4217"
+ style="stop-color:#000000"
+ offset="0" />
+ <stop
+ id="stop4219"
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" />
+ <a:midPointStop
+ style="stop-color:#000000"
+ offset="0" />
+ <a:midPointStop
+ style="stop-color:#000000"
+ offset="0.6933" />
+ <a:midPointStop
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" />
+ </linearGradient>
+
+ <linearGradient
+ y2="155.3268"
+ x2="622.7832"
+ y1="239.7637"
+ x1="622.7832"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4223">
+ <stop
+ id="stop4225"
+ style="stop-color:#000000"
+ offset="0" />
+ <stop
+ id="stop4227"
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" />
+ <a:midPointStop
+ style="stop-color:#000000"
+ offset="0" />
+ <a:midPointStop
+ style="stop-color:#000000"
+ offset="0.6933" />
+ <a:midPointStop
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" />
+ </linearGradient>
+
+ <linearGradient
+ y2="155.3275"
+ x2="655.6709"
+ y1="239.7637"
+ x1="655.6709"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4231">
+ <stop
+ id="stop4233"
+ style="stop-color:#000000"
+ offset="0" />
+ <stop
+ id="stop4235"
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" />
+ <a:midPointStop
+ style="stop-color:#000000"
+ offset="0" />
+ <a:midPointStop
+ style="stop-color:#000000"
+ offset="0.6933" />
+ <a:midPointStop
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" />
+ </linearGradient>
+
+ <linearGradient
+ y2="155.3277"
+ x2="697.92969"
+ y1="239.7637"
+ x1="697.92969"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4239">
+ <stop
+ id="stop4241"
+ style="stop-color:#000000"
+ offset="0" />
+ <stop
+ id="stop4243"
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" />
+ <a:midPointStop
+ style="stop-color:#000000"
+ offset="0" />
+ <a:midPointStop
+ style="stop-color:#000000"
+ offset="0.6933" />
+ <a:midPointStop
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" />
+ </linearGradient>
+
+ </g>
+</g></svg> \ No newline at end of file
diff --git a/media/switch_logo.eps b/media/switch_logo.eps
deleted file mode 100644
index 741970c1..00000000
--- a/media/switch_logo.eps
+++ /dev/null
Binary files differ
diff --git a/media/switch_logo.jpeg b/media/switch_logo.jpeg
deleted file mode 100644
index 7c05cd94..00000000
--- a/media/switch_logo.jpeg
+++ /dev/null
Binary files differ
diff --git a/media/switch_logo.png b/media/switch_logo.png
new file mode 100644
index 00000000..1e072247
--- /dev/null
+++ b/media/switch_logo.png
Binary files differ
diff --git a/scons-tools/emscripten.py b/scons-tools/emscripten.py
index af85f106..94153adb 100755
--- a/scons-tools/emscripten.py
+++ b/scons-tools/emscripten.py
@@ -9,11 +9,6 @@ from SCons.Scanner import Scanner
def exists(env):
return True
-def _expand_settings_flags(settings, env):
- return [
- ('-s%s=%s' % (KEY, json.dumps(VALUE).replace('"', '\\"')))
- for KEY, VALUE in settings.items() ]
-
emscripten_version_files = {}
def build_version_file(env):
@@ -86,10 +81,6 @@ def emscripten(env, target_js, source_bc):
buildName('raw.js'),
[opt_ll])
- [optimized_js] = env.JSOptimizer(
- buildName('opt.js'),
- raw_emscripten_js)
-
prejs = [
env['EMSCRIPTEN_PREJS'],
'${EMSCRIPTEN_HOME}/src/embind/emval.js',
@@ -98,7 +89,7 @@ def emscripten(env, target_js, source_bc):
[concatenated_js] = env.Concatenate(
buildName('concat.js'),
[ prejs,
- optimized_js,
+ raw_emscripten_js,
env['EMSCRIPTEN_POSTJS'] ])
DISABLE_EMSCRIPTEN_WARNINGS = [
@@ -139,11 +130,6 @@ def emscripten(env, target_js, source_bc):
concatenated_js,
CLOSURE_FLAGS=['--language_in', 'ECMASCRIPT5']+DISABLE_EMSCRIPTEN_WARNINGS+['--formatting', 'PRETTY_PRINT', '--compilation_level', 'ADVANCED_OPTIMIZATIONS'])
- [global_emscripten_min_js] = env.JSOptimizer(
- buildName('global.min.js'),
- closure_js,
- JS_OPTIMIZER_PASSES=['simplifyExpressionsPost', 'minifyWhitespace', 'last'])
-
[emscripten_iteration_js] = env.WrapInModule(
buildName('iteration.js'),
iter_global_emscripten_js)
@@ -154,12 +140,27 @@ def emscripten(env, target_js, source_bc):
[emscripten_min_js] = env.WrapInModule(
buildName('min.js'),
- global_emscripten_min_js)
+ closure_js)
return [emscripten_iteration_js, emscripten_js, emscripten_min_js]
LIBC_SOURCES = [
'system/lib/dlmalloc.c',
+ 'system/lib/libc/musl/src/internal/floatscan.c',
+ 'system/lib/libc/musl/src/internal/shgetc.c',
+ 'system/lib/libc/musl/src/math/scalbn.c',
+ 'system/lib/libc/musl/src/math/scalbnl.c',
+ 'system/lib/libc/musl/src/stdio/__overflow.c',
+ 'system/lib/libc/musl/src/stdio/__toread.c',
+ 'system/lib/libc/musl/src/stdio/__towrite.c',
+ 'system/lib/libc/musl/src/stdio/__uflow.c',
+ 'system/lib/libc/musl/src/stdlib/atof.c',
+ 'system/lib/libc/musl/src/stdlib/strtod.c',
+ 'system/lib/libc/musl/src/string/memcmp.c',
+ 'system/lib/libc/musl/src/string/strcasecmp.c',
+ 'system/lib/libc/musl/src/string/strcmp.c',
+ 'system/lib/libc/musl/src/string/strncasecmp.c',
+ 'system/lib/libc/musl/src/string/strncmp.c',
'system/lib/libc/musl/src/string/wmemset.c',
'system/lib/libc/musl/src/string/wmemcpy.c',
]
@@ -185,13 +186,14 @@ LIBCXX_SOURCES = [os.path.join('system/lib/libcxx', x) for x in [
'strstream.cpp',
'system_error.cpp',
#'thread.cpp',
- 'typeinfo.cpp',
+ #'typeinfo.cpp',
'utility.cpp',
'valarray.cpp',
]]
LIBCXXABI_SOURCES = [os.path.join('system/lib/libcxxabi/src', x) for x in [
- 'private_typeinfo.cpp'
+ 'private_typeinfo.cpp',
+ 'typeinfo.cpp'
]]
# MAJOR HACK ALERT
@@ -233,6 +235,7 @@ def build_libcxx(env):
env = env.Clone()
env['CXXFLAGS'] = filter(lambda e: e not in ('-Werror', '-Wall'), env['CXXFLAGS'])
env['CCFLAGS'] = filter(lambda e: e not in ('-Werror', '-Wall'), env['CCFLAGS'])
+ env['CCFLAGS'] = env['CCFLAGS'] + ['-isystem${EMSCRIPTEN_HOME}/system/lib/libc/musl/src/internal/']
objs = [
env.Object(
@@ -248,16 +251,10 @@ def build_libcxx(env):
def generate(env):
env.SetDefault(
PYTHON=sys.executable,
- NODEJS='node',
- JS_ENGINE='$NODEJS',
- EMSCRIPTEN_FLAGS=['-v', '-j', '--suppressUsageWarning'],
+ EMSCRIPTEN_FLAGS=[],
EMSCRIPTEN_TEMP_DIR=env.Dir('#/emscripten.tmp'),
- _expand_settings_flags=_expand_settings_flags,
EMSCRIPTEN_PREJS=[],
EMSCRIPTEN_POSTJS=[],
- EMSCRIPTEN_SETTINGS={},
- _EMSCRIPTEN_SETTINGS_FLAGS='${_expand_settings_flags(EMSCRIPTEN_SETTINGS, __env__)}',
- JS_OPTIMIZER_PASSES=[],
LLVM_OPT_PASSES=['-std-compile-opts', '-std-link-opts'],
EMSCRIPTEN_HOME=env.Dir(os.path.join(os.path.dirname(__file__), '..')),
@@ -274,7 +271,7 @@ def generate(env):
RANLIBCOM='',
CCFLAGS=[
'-U__STRICT_ANSI__',
- '-target', 'asmjs-unknown-emscripten',
+ '-target', 'le32-unknown-nacl',
'-nostdinc',
'-Wno-#warnings',
'-Wno-error=unused-variable',
@@ -286,10 +283,9 @@ def generate(env):
'-Xclang', '-nostdinc++',
'-Xclang', '-nobuiltininc',
'-Xclang', '-nostdsysteminc',
- '-Xclang', '-isystem$EMSCRIPTEN_HOME/system/include',
+ '-Xclang', '-isystem$EMSCRIPTEN_HOME/system/include/compat',
'-Xclang', '-isystem$EMSCRIPTEN_HOME/system/include/libc',
'-Xclang', '-isystem$EMSCRIPTEN_HOME/system/include/libcxx',
- '-Xclang', '-isystem$EMSCRIPTEN_HOME/system/include/bsd',
'-emit-llvm'],
CXXFLAGS=['-std=c++11', '-fno-exceptions'],
)
@@ -307,11 +303,7 @@ def generate(env):
)
env['BUILDERS']['Emscripten'] = Builder(
- action='$PYTHON ${EMSCRIPTEN_HOME}/emscripten.py $EMSCRIPTEN_FLAGS $_EMSCRIPTEN_SETTINGS_FLAGS --temp-dir=$EMSCRIPTEN_TEMP_DIR --compiler $JS_ENGINE --relooper=third-party/relooper.js $SOURCE > $TARGET',
- target_scanner=EmscriptenScanner)
-
- env['BUILDERS']['JSOptimizer'] = Builder(
- action='$JS_ENGINE ${EMSCRIPTEN_HOME}/tools/js-optimizer.js $SOURCE $JS_OPTIMIZER_PASSES > $TARGET',
+ action='$PYTHON ${EMSCRIPTEN_HOME}/emcc ${EMSCRIPTEN_FLAGS} $SOURCE -o $TARGET',
target_scanner=EmscriptenScanner)
def depend_on_embedder(target, source, env):
diff --git a/src/closure-externs.js b/src/closure-externs.js
index a82aa669..fe6d84aa 100644
--- a/src/closure-externs.js
+++ b/src/closure-externs.js
@@ -108,3 +108,63 @@ var flags = {};
flags.binary;
+/**
+ * @fileoverview Definitions for W3C's Gamepad specification.
+ * @see http://www.w3.org/TR/gamepad/
+ * @externs
+ */
+
+/**
+ * @typedef {{id: string, index: number, timestamp: number, axes: Array.<number>, buttons: Array.<number>}}
+ */
+var Gamepad;
+
+/**
+* @type {Array.<number>}
+*/
+Gamepad.buttons;
+
+/**
+* @type {Array.<number>}
+*/
+Gamepad.axes;
+
+/**
+* @type {number}
+*/
+Gamepad.index;
+
+/**
+* @type {string}
+*/
+Gamepad.id;
+
+/**
+* @type {number}
+*/
+Gamepad.timestamp;
+
+/**
+ * @return {Array.<Gamepad>}
+ */
+navigator.getGamepads = function() {};
+
+/**
+ * @return {Array.<Gamepad>}
+ */
+navigator.webkitGetGamepads = function() {};
+
+/**
+ * @return {Array.<Gamepad>}
+ */
+navigator.webkitGamepads = function() {};
+
+/**
+ * @return {Array.<Gamepad>}
+ */
+navigator.mozGamepads = function() {};
+
+/**
+ * @return {Array.<Gamepad>}
+ */
+navigator.gamepads = function() {};
diff --git a/src/library.js b/src/library.js
index 2c3d5e03..8aac2bb2 100644
--- a/src/library.js
+++ b/src/library.js
@@ -6402,6 +6402,13 @@ LibraryManager.library = {
siginterrupt: function() { throw 'siginterrupt not implemented' },
+ raise__deps: ['$ERRNO_CODES', '__setErrNo'],
+ raise: function(sig) {
+ // TODO:
+ ___setErrNo(ERRNO_CODES.ENOSYS);
+ return -1;
+ },
+
// ==========================================================================
// sys/wait.h
// ==========================================================================
@@ -8169,7 +8176,9 @@ LibraryManager.library = {
},
setsockopt: function(d, level, optname, optval, optlen) {
+#if SOCKET_DEBUG
console.log('ignoring setsockopt command');
+#endif
return 0;
},
@@ -8658,7 +8667,9 @@ LibraryManager.library = {
},
setsockopt: function(fd, level, optname, optval, optlen) {
+#if SOCKET_DEBUG
console.log('ignoring setsockopt command');
+#endif
return 0;
},
@@ -8915,10 +8926,19 @@ LibraryManager.library = {
emscripten_get_callstack_js: function(flags) {
var err = new Error();
if (!err.stack) {
- Runtime.warnOnce('emscripten_get_callstack_js is not supported on this browser!');
- return '';
+ // IE10+ special cases: It does have callstack info, but it is only populated if an Error object is thrown,
+ // so try that as a special-case.
+ try {
+ throw new Error(0);
+ } catch(e) {
+ err = e;
+ }
+ if (!err.stack) {
+ Runtime.warnOnce('emscripten_get_callstack_js is not supported on this browser!');
+ return '';
+ }
}
- var callstack = new Error().stack.toString();
+ var callstack = err.stack.toString();
// Find the symbols in the callstack that corresponds to the functions that report callstack information, and remove everyhing up to these from the output.
var iThisFunc = callstack.lastIndexOf('_emscripten_log');
diff --git a/src/library_browser.js b/src/library_browser.js
index a280d1b2..0808b9f0 100644
--- a/src/library_browser.js
+++ b/src/library_browser.js
@@ -723,8 +723,59 @@ mergeInto(LibraryManager.library, {
// PROGRESS
http.onprogress = function http_onprogress(e) {
- var percentComplete = (e.position / e.totalSize)*100;
- if (onprogress) Runtime.dynCall('vii', onprogress, [arg, percentComplete]);
+ if (e.lengthComputable || (e.lengthComputable === undefined && e.totalSize != 0)) {
+ var percentComplete = (e.position / e.totalSize)*100;
+ if (onprogress) Runtime.dynCall('vii', onprogress, [arg, percentComplete]);
+ }
+ };
+
+ // Useful because the browser can limit the number of redirection
+ try {
+ if (http.channel instanceof Ci.nsIHttpChannel)
+ http.channel.redirectionLimit = 0;
+ } catch (ex) { /* whatever */ }
+
+ if (_request == "POST") {
+ //Send the proper header information along with the request
+ http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+ http.setRequestHeader("Content-length", _param.length);
+ http.setRequestHeader("Connection", "close");
+ http.send(_param);
+ } else {
+ http.send(null);
+ }
+ },
+
+ emscripten_async_wget2_data: function(url, request, param, arg, free, onload, onerror, onprogress) {
+ var _url = Pointer_stringify(url);
+ var _request = Pointer_stringify(request);
+ var _param = Pointer_stringify(param);
+
+ var http = new XMLHttpRequest();
+ http.open(_request, _url, true);
+ http.responseType = 'arraybuffer';
+
+ // LOAD
+ http.onload = function http_onload(e) {
+ if (http.status == 200 || _url.substr(0,4).toLowerCase() != "http") {
+ var byteArray = new Uint8Array(http.response);
+ var buffer = _malloc(byteArray.length);
+ HEAPU8.set(byteArray, buffer);
+ if (onload) Runtime.dynCall('viii', onload, [arg, buffer, byteArray.length]);
+ if (free) _free(buffer);
+ } else {
+ if (onerror) Runtime.dynCall('viii', onerror, [arg, http.status, http.statusText]);
+ }
+ };
+
+ // ERROR
+ http.onerror = function http_onerror(e) {
+ if (onerror) Runtime.dynCall('viii', onerror, [arg, http.status, http.statusText]);
+ };
+
+ // PROGRESS
+ http.onprogress = function http_onprogress(e) {
+ if (onprogress) Runtime.dynCall('viii', onprogress, [arg, e.loaded, e.lengthComputable || e.lengthComputable === undefined ? e.total : 0]);
};
// Useful because the browser can limit the number of redirection
diff --git a/src/library_egl.js b/src/library_egl.js
index e2d1df43..46ec939e 100644
--- a/src/library_egl.js
+++ b/src/library_egl.js
@@ -492,7 +492,11 @@ var LibraryEGL = {
EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
return 1;
},
-
+
+
+ // EGLAPI EGLBoolean EGLAPIENTRY eglWaitGL(void);
+ eglWaitGL: 'eglWaitClient',
+
// EGLAPI EGLBoolean EGLAPIENTRY eglSwapInterval(EGLDisplay dpy, EGLint interval);
eglSwapInterval: function(display, interval) {
if (display != 62000 /* Magic ID for Emscripten 'default display' */) {
diff --git a/src/library_glfw.js b/src/library_glfw.js
index b54205ad..e5782900 100644
--- a/src/library_glfw.js
+++ b/src/library_glfw.js
@@ -51,7 +51,7 @@ var LibraryGLFW = {
DOMToGLFWKeyCode: function(keycode) {
switch (keycode) {
case 0x09: return 295 ; //DOM_VK_TAB -> GLFW_KEY_TAB
- case 0x1B: return 255 ; //DOM_VK_ESCAPE -> GLFW_KEY_ESC
+ case 0x1B: return 257 ; //DOM_VK_ESCAPE -> GLFW_KEY_ESC
case 0x6A: return 313 ; //DOM_VK_MULTIPLY -> GLFW_KEY_KP_MULTIPLY
case 0x6B: return 315 ; //DOM_VK_ADD -> GLFW_KEY_KP_ADD
case 0x6D: return 314 ; //DOM_VK_SUBTRACT -> GLFW_KEY_KP_SUBTRACT
diff --git a/src/library_html5.js b/src/library_html5.js
index b17a0d4d..d9376c2a 100644
--- a/src/library_html5.js
+++ b/src/library_html5.js
@@ -17,6 +17,11 @@ var LibraryJSEvents = {
// so that we can report information about that element in the event message.
previousFullscreenElement: null,
+ // Remember the current mouse coordinates in case we need to emulate movementXY generation for browsers that don't support it.
+ // Some browsers (e.g. Safari 6.0.5) only give movementXY when Pointerlock is active.
+ previousScreenX: null,
+ previousScreenY: null,
+
// When the C runtime exits via exit(), we unregister all event handlers added by this library to be nice and clean.
// Track in this field whether we have yet registered that __ATEXIT__ handler.
removeEventListenersRegistered: false,
@@ -118,8 +123,11 @@ var LibraryJSEvents = {
// Stores objects representing each currently registered JS event handler.
eventHandlers: [],
+ isInternetExplorer: function() { return navigator.userAgent.indexOf('MSIE') !== -1 || navigator.appVersion.indexOf('Trident/') > 0; },
+
_removeHandler: function(i) {
- JSEvents.eventHandlers[i].target.removeEventListener(JSEvents.eventHandlers[i].eventTypeString, JSEvents.eventHandlers[i].handlerFunc, true);
+ var h = JSEvents.eventHandlers[i];
+ h.target.removeEventListener(h.eventTypeString, h.eventListenerFunc, h.useCapture);
JSEvents.eventHandlers.splice(i, 1);
},
@@ -139,6 +147,7 @@ var LibraryJSEvents = {
}
if (eventHandler.callbackfunc) {
+ eventHandler.eventListenerFunc = jsEventHandler;
eventHandler.target.addEventListener(eventHandler.eventTypeString, jsEventHandler, eventHandler.useCapture);
JSEvents.eventHandlers.push(eventHandler);
JSEvents.registerRemoveEventListeners();
@@ -177,11 +186,9 @@ var LibraryJSEvents = {
}
};
- var isInternetExplorer = (navigator.userAgent.indexOf('MSIE') !== -1 || navigator.appVersion.indexOf('Trident/') > 0);
-
var eventHandler = {
target: JSEvents.findEventTarget(target),
- allowsDeferredCalls: isInternetExplorer ? false : true, // MSIE doesn't allow fullscreen and pointerlock requests from key handlers, others do.
+ allowsDeferredCalls: JSEvents.isInternetExplorer() ? false : true, // MSIE doesn't allow fullscreen and pointerlock requests from key handlers, others do.
eventTypeString: eventTypeString,
callbackfunc: callbackfunc,
handlerFunc: handlerFunc,
@@ -203,10 +210,12 @@ var LibraryJSEvents = {
{{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.metaKey, 'e.metaKey', 'i32') }}};
{{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.button, 'e.button', 'i16') }}};
{{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.buttons, 'e.buttons', 'i16') }}};
- {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.movementX, 'e.movementX || e.mozMovementX || e.webkitMovementX', 'i32') }}};
- {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.movementY, 'e.movementY || e.mozMovementY || e.webkitMovementY', 'i32') }}};
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.movementX, 'e["movementX"] || e["mozMovementX"] || e["webkitMovementX"] || (e.screenX-JSEvents.previousScreenX)', 'i32') }}};
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.movementY, 'e["movementY"] || e["mozMovementY"] || e["webkitMovementY"] || (e.screenY-JSEvents.previousScreenY)', 'i32') }}};
{{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.canvasX, 'e.clientX - rect.left', 'i32') }}};
{{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.canvasY, 'e.clientY - rect.top', 'i32') }}};
+ JSEvents.previousScreenX = e.screenX;
+ JSEvents.previousScreenY = e.screenY;
},
registerMouseEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) {
@@ -230,6 +239,8 @@ var LibraryJSEvents = {
handlerFunc: handlerFunc,
useCapture: useCapture
};
+ // In IE, mousedown events don't either allow deferred calls to be run!
+ if (JSEvents.isInternetExplorer() && eventTypeString == 'mousedown') eventHandler.allowsDeferredCalls = false;
JSEvents.registerOrRemoveHandler(eventHandler);
},
@@ -237,13 +248,27 @@ var LibraryJSEvents = {
if (!JSEvents.wheelEvent) {
JSEvents.wheelEvent = _malloc( {{{ C_STRUCTS.EmscriptenWheelEvent.__size__ }}} );
}
- var handlerFunc = function(event) {
+ // The DOM Level 3 events spec event 'wheel'
+ var wheelHandlerFunc = function(event) {
var e = event || window.event;
JSEvents.fillMouseEventData(JSEvents.wheelEvent, e);
- {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaX, 'e.deltaX', 'double') }}};
- {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaY, 'e.deltaY', 'double') }}};
- {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaZ, 'e.deltaZ', 'double') }}};
- {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaMode, 'e.deltaMode', 'i32') }}};
+ {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaX, 'e["deltaX"]', 'double') }}};
+ {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaY, 'e["deltaY"]', 'double') }}};
+ {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaZ, 'e["deltaZ"]', 'double') }}};
+ {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaMode, 'e["deltaMode"]', 'i32') }}};
+ var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.wheelEvent, userData]);
+ if (shouldCancel) {
+ e.preventDefault();
+ }
+ };
+ // The 'mousewheel' event as implemented in Safari 6.0.5
+ var mouseWheelHandlerFunc = function(event) {
+ var e = event || window.event;
+ JSEvents.fillMouseEventData(JSEvents.wheelEvent, e);
+ {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaX, 'e["wheelDeltaX"]', 'double') }}};
+ {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaY, '-e["wheelDeltaY"] /* Invert to unify direction with the DOM Level 3 wheel event. */', 'double') }}};
+ {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaZ, '0 /* Not available */', 'double') }}};
+ {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaMode, '0 /* DOM_DELTA_PIXEL */', 'i32') }}};
var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.wheelEvent, userData]);
if (shouldCancel) {
e.preventDefault();
@@ -255,7 +280,7 @@ var LibraryJSEvents = {
allowsDeferredCalls: true,
eventTypeString: eventTypeString,
callbackfunc: callbackfunc,
- handlerFunc: handlerFunc,
+ handlerFunc: (eventTypeString == 'wheel') ? wheelHandlerFunc : mouseWheelHandlerFunc,
useCapture: useCapture
};
JSEvents.registerOrRemoveHandler(eventHandler);
@@ -918,8 +943,16 @@ var LibraryJSEvents = {
},
emscripten_set_wheel_callback: function(target, userData, useCapture, callbackfunc) {
- JSEvents.registerWheelEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_WHEEL') }}}, "wheel");
- return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ target = JSEvents.findEventTarget(target);
+ if (typeof target.onwheel !== 'undefined') {
+ JSEvents.registerWheelEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_WHEEL') }}}, "wheel");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ } else if (typeof target.onmousewheel !== 'undefined') {
+ JSEvents.registerWheelEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_WHEEL') }}}, "mousewheel");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ } else {
+ return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}};
+ }
},
emscripten_set_resize_callback: function(target, userData, useCapture, callbackfunc) {
diff --git a/src/library_openal.js b/src/library_openal.js
index fd382aa1..58b11607 100644
--- a/src/library_openal.js
+++ b/src/library_openal.js
@@ -58,9 +58,21 @@ var LibraryOpenAL = {
entry.src = AL.currentContext.ctx.createBufferSource();
entry.src.buffer = entry.buffer;
entry.src.connect(src.gain);
- entry.src.start(startTime, offset);
-
+ if (typeof(entry.src.start) !== 'undefined') {
+ entry.src.start(startTime, offset);
+ } else if (typeof(entry.src.noteOn) !== 'undefined') {
+ entry.src.noteOn(startTime);
#if OPENAL_DEBUG
+ if (offset > 0) {
+ Runtime.warnOnce('The current browser does not support AudioBufferSourceNode.start(when, offset); method, so cannot play back audio with an offset '+offset+' secs! Audio glitches will occur!');
+ }
+#endif
+ }
+#if OPENAL_DEBUG
+ else {
+ Runtime.warnOnce('Unable to start AudioBufferSourceNode playback! Not supported by the browser?');
+ }
+
console.log('updateSource queuing buffer ' + i + ' for source ' + idx + ' at ' + startTime + ' (offset by ' + offset + ')');
#endif
}
@@ -204,6 +216,9 @@ var LibraryOpenAL = {
}
if (ctx) {
+ // Old Web Audio API (e.g. Safari 6.0.5) had an inconsistently named createGainNode function.
+ if (typeof(ctx.createGain) === 'undefined') ctx.createGain = ctx.createGainNode;
+
var gain = ctx.createGain();
gain.connect(ctx.destination);
var context = {
@@ -1283,16 +1298,16 @@ var LibraryOpenAL = {
ret = 'Out of Memory';
break;
case 0x1004 /* ALC_DEFAULT_DEVICE_SPECIFIER */:
- if (typeof(AudioContext) == "function" ||
- typeof(webkitAudioContext) == "function") {
+ if (typeof(AudioContext) !== "undefined" ||
+ typeof(webkitAudioContext) !== "undefined") {
ret = 'Device';
} else {
return 0;
}
break;
case 0x1005 /* ALC_DEVICE_SPECIFIER */:
- if (typeof(AudioContext) == "function" ||
- typeof(webkitAudioContext) == "function") {
+ if (typeof(AudioContext) !== "undefined" ||
+ typeof(webkitAudioContext) !== "undefined") {
ret = 'Device\0';
} else {
ret = '\0';
diff --git a/src/library_sdl.js b/src/library_sdl.js
index ac43f3b4..3d96a693 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -1811,9 +1811,9 @@ var LibrarySDL = {
// Initialize Web Audio API if we haven't done so yet. Note: Only initialize Web Audio context ever once on the web page,
// since initializing multiple times fails on Chrome saying 'audio resources have been exhausted'.
if (!SDL.audioContext) {
- if (typeof(AudioContext) === 'function') {
+ if (typeof(AudioContext) !== 'undefined') {
SDL.audioContext = new AudioContext();
- } else if (typeof(webkitAudioContext) === 'function') {
+ } else if (typeof(webkitAudioContext) !== 'undefined') {
SDL.audioContext = new webkitAudioContext();
} else {
throw 'Web Audio API is not available!';
@@ -1856,7 +1856,12 @@ var LibrarySDL = {
}
#endif
var playtime = Math.max(curtime, SDL.audio.nextPlayTime);
- SDL.audio.soundSource[SDL.audio.nextSoundSource]['start'](playtime);
+ var ss = SDL.audio.soundSource[SDL.audio.nextSoundSource];
+ if (typeof ss['start'] !== 'undefined') {
+ ss['start'](playtime);
+ } else if (typeof ss['noteOn'] !== 'undefined') {
+ ss['noteOn'](playtime);
+ }
var buffer_duration = sizeSamplesPerChannel / SDL.audio.freq;
SDL.audio.nextPlayTime = playtime + buffer_duration;
// Timer will be scheduled before the buffer completed playing.
@@ -1929,7 +1934,7 @@ var LibrarySDL = {
} else if (!SDL.audio.timer && !SDL.audio.scriptProcessorNode) {
// If we are using the same sampling frequency as the native sampling rate of the Web Audio graph is using, we can feed our buffers via
// Web Audio ScriptProcessorNode, which is a pull-mode API that calls back to our code to get audio data.
- if (SDL.audioContext !== undefined && SDL.audio.freq == SDL.audioContext['sampleRate']) {
+ if (SDL.audioContext !== undefined && SDL.audio.freq == SDL.audioContext['sampleRate'] && typeof SDL.audioContext['createScriptProcessor'] !== 'undefined') {
var sizeSamplesPerChannel = SDL.audio.bufferSize / SDL.audio.bytesPerSample / SDL.audio.channels; // How many samples per a single channel fit in the cb buffer?
SDL.audio.scriptProcessorNode = SDL.audioContext['createScriptProcessor'](sizeSamplesPerChannel, 0, SDL.audio.channels);
SDL.audio.scriptProcessorNode['onaudioprocess'] = function (e) {
diff --git a/src/library_sockfs.js b/src/library_sockfs.js
index 22fd8761..23641464 100644
--- a/src/library_sockfs.js
+++ b/src/library_sockfs.js
@@ -133,12 +133,42 @@ mergeInto(LibraryManager.library, {
} else {
// create the actual websocket object and connect
try {
- var url = 'ws://' + addr + ':' + port;
+ // runtimeConfig gets set to true if WebSocket runtime configuration is available.
+ var runtimeConfig = (Module['websocket'] && ('object' === typeof Module['websocket']));
+
+ // The default value is 'ws://' the replace is needed because the compiler replaces "//" comments with '#'
+ // comments without checking context, so we'd end up with ws:#, the replace swaps the "#" for "//" again.
+ var url = '{{{ WEBSOCKET_URL }}}'.replace('#', '//');
+
+ if (runtimeConfig) {
+ if ('string' === typeof Module['websocket']['url']) {
+ url = Module['websocket']['url']; // Fetch runtime WebSocket URL config.
+ }
+ }
+
+ if (url === 'ws://' || url === 'wss://') { // Is the supplied URL config just a prefix, if so complete it.
+ url = url + addr + ':' + port;
+ }
+
+ // Make the WebSocket subprotocol (Sec-WebSocket-Protocol) default to binary if no configuration is set.
+ var subProtocols = '{{{ WEBSOCKET_SUBPROTOCOL }}}'; // The default value is 'binary'
+
+ if (runtimeConfig) {
+ if ('string' === typeof Module['websocket']['subprotocol']) {
+ subProtocols = Module['websocket']['subprotocol']; // Fetch runtime WebSocket subprotocol config.
+ }
+ }
+
+ // The regex trims the string (removes spaces at the beginning and end, then splits the string by
+ // <any space>,<any space> into an Array. Whitespace removal is important for Websockify and ws.
+ subProtocols = subProtocols.replace(/^ +| +$/g,"").split(/ *, */);
+
+ // The node ws library API for specifying optional subprotocol is slightly different than the browser's.
+ var opts = ENVIRONMENT_IS_NODE ? {'protocol': subProtocols.toString()} : subProtocols;
+
#if SOCKET_DEBUG
- console.log('connect: ' + url);
+ Module.print('connect: ' + url + ', ' + subProtocols.toString());
#endif
- // the node ws library API is slightly different than the browser's
- var opts = ENVIRONMENT_IS_NODE ? {headers: {'websocket-protocol': ['binary']}} : ['binary'];
// If node we use the ws library.
var WebSocket = ENVIRONMENT_IS_NODE ? require('ws') : window['WebSocket'];
ws = new WebSocket(url, opts);
diff --git a/src/postamble.js b/src/postamble.js
index 603b330c..b90049bc 100644
--- a/src/postamble.js
+++ b/src/postamble.js
@@ -48,10 +48,6 @@ Module['callMain'] = Module.callMain = function callMain(args) {
args = args || [];
- if (ENVIRONMENT_IS_WEB && preloadStartTime !== null) {
- Module.printErr('preload time: ' + (Date.now() - preloadStartTime) + ' ms');
- }
-
ensureInitRuntime();
var argc = args.length+1;
@@ -130,6 +126,10 @@ function run(args) {
preMain();
+ if (ENVIRONMENT_IS_WEB && preloadStartTime !== null) {
+ Module.printErr('pre-main prep time: ' + (Date.now() - preloadStartTime) + ' ms');
+ }
+
if (Module['_main'] && shouldRunNow) {
Module['callMain'](args);
}
diff --git a/src/preamble.js b/src/preamble.js
index 28a1dcbc..89ab5026 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -187,16 +187,16 @@ function SAFE_HEAP_STORE(dest, value, bytes, isFloat) {
Module.print('SAFE_HEAP store: ' + [dest, value, bytes, isFloat]);
#endif
assert(dest > 0, 'segmentation fault');
- assert(dest % bytes === 0);
- assert(dest < Math.max(DYNAMICTOP, STATICTOP));
+ assert(dest % bytes === 0, 'alignment error');
+ assert(dest < Math.max(DYNAMICTOP, STATICTOP), 'segmentation fault (high)');
assert(DYNAMICTOP <= TOTAL_MEMORY);
setValue(dest, value, getSafeHeapType(bytes, isFloat), 1);
}
function SAFE_HEAP_LOAD(dest, bytes, isFloat, unsigned) {
assert(dest > 0, 'segmentation fault');
- assert(dest % bytes === 0);
- assert(dest < Math.max(DYNAMICTOP, STATICTOP));
+ assert(dest % bytes === 0, 'alignment error');
+ assert(dest < Math.max(DYNAMICTOP, STATICTOP), 'segmentation fault (high)');
assert(DYNAMICTOP <= TOTAL_MEMORY);
var type = getSafeHeapType(bytes, isFloat);
var ret = getValue(dest, type, 1);
@@ -805,7 +805,14 @@ function demangle(func) {
}
}
if (!allowVoid && list.length === 1 && list[0] === 'void') list = []; // avoid (void)
- return rawList ? list : ret + flushList();
+ if (rawList) {
+ if (ret) {
+ list.push(ret + '?');
+ }
+ return list;
+ } else {
+ return ret + flushList();
+ }
}
try {
// Special-case the entry point, since its name differs from other name mangling.
diff --git a/src/relooper/Relooper.cpp b/src/relooper/Relooper.cpp
index c11de099..780a6d59 100644
--- a/src/relooper/Relooper.cpp
+++ b/src/relooper/Relooper.cpp
@@ -122,7 +122,7 @@ void Branch::Render(Block *Target, bool SetLabel) {
if (Code) PrintIndented("%s\n", Code);
if (SetLabel) PrintIndented("label = %d;\n", Target->Id);
if (Ancestor) {
- if (Type != Direct) {
+ if (Type == Break || Type == Continue) {
if (Labeled) {
PrintIndented("%s L%d;\n", Type == Break ? "break" : "continue", Ancestor->Id);
} else {
@@ -287,6 +287,11 @@ void Block::Render(bool InLoop) {
Details->Render(Target, SetCurrLabel);
if (HasFusedContent) {
Fused->InnerMap.find(Target)->second->Render(InLoop);
+ } else if (Details->Type == Branch::Nested) {
+ // Nest the parent content here, and remove it from showing up afterwards as Next
+ assert(Parent->Next);
+ Parent->Next->Render(InLoop);
+ Parent->Next = NULL;
}
if (useSwitch && iter != ProcessedBranchesOut.end()) {
PrintIndented("break;\n");
@@ -650,7 +655,7 @@ void Relooper::Calculate(Block *Entry) {
Block *Curr = *iter;
for (BlockBranchMap::iterator iter = Curr->BranchesOut.begin(); iter != Curr->BranchesOut.end(); iter++) {
Block *Target = iter->first;
- if (!contains(Hoisted, Target) && !contains(NextEntries, Target))
+ if (!contains(Hoisted, Target) && !contains(NextEntries, Target)) {
// abort this hoisting
abort = true;
break;
@@ -1077,12 +1082,48 @@ void Relooper::Calculate(Block *Entry) {
SHAPE_SWITCH(Root, {
if (Simple->Inner->BranchVar) LastLoop = NULL; // a switch clears out the loop (TODO: only for breaks, not continue)
- // If there is a next block, we already know at Simple creation time to make direct branches,
- // and we can do nothing more. If there is no next however, then Natural is where we will
- // go to by doing nothing, so we can potentially optimize some branches to direct.
if (Simple->Next) {
+ if (!Simple->Inner->BranchVar && Simple->Inner->ProcessedBranchesOut.size() == 2) {
+ // If there is a next block, we already know at Simple creation time to make direct branches,
+ // and we can do nothing more in general. But, we try to optimize the case of a break and
+ // a direct: This would normally be if (break?) { break; } .. but if we
+ // make sure to nest the else, we can save the break, if (!break?) { .. } . This is also
+ // better because the more canonical nested form is easier to further optimize later. The
+ // downside is more nesting, which adds to size in builds with whitespace.
+ // Note that we avoid switches, as it complicates control flow and is not relevant
+ // for the common case we optimize here.
+ bool Found = false;
+ bool Abort = false;
+ for (BlockBranchMap::iterator iter = Simple->Inner->ProcessedBranchesOut.begin(); iter != Simple->Inner->ProcessedBranchesOut.end(); iter++) {
+ Block *Target = iter->first;
+ Branch *Details = iter->second;
+ if (Details->Type == Branch::Break) {
+ Found = true;
+ if (!contains(NaturalBlocks, Target)) Abort = true;
+ } else if (Details->Type != Branch::Direct) {
+ Abort = true;
+ }
+ }
+ if (Found && !Abort) {
+ for (BlockBranchMap::iterator iter = Simple->Inner->ProcessedBranchesOut.begin(); iter != Simple->Inner->ProcessedBranchesOut.end(); iter++) {
+ Block *Target = iter->first;
+ Branch *Details = iter->second;
+ if (Details->Type == Branch::Break) {
+ Details->Type = Branch::Direct;
+ if (MultipleShape *Multiple = Shape::IsMultiple(Details->Ancestor)) {
+ Multiple->NeedLoop--;
+ }
+ } else {
+ assert(Details->Type == Branch::Direct);
+ Details->Type = Branch::Nested;
+ }
+ }
+ }
+ }
Next = Simple->Next;
} else {
+ // If there is no next then Natural is where we will
+ // go to by doing nothing, so we can potentially optimize some branches to direct.
for (BlockBranchMap::iterator iter = Simple->Inner->ProcessedBranchesOut.begin(); iter != Simple->Inner->ProcessedBranchesOut.end(); iter++) {
Block *Target = iter->first;
Branch *Details = iter->second;
@@ -1140,7 +1181,7 @@ void Relooper::Calculate(Block *Entry) {
for (BlockBranchMap::iterator iter = Simple->Inner->ProcessedBranchesOut.begin(); iter != Simple->Inner->ProcessedBranchesOut.end(); iter++) {
Block *Target = iter->first;
Branch *Details = iter->second;
- if (Details->Type != Branch::Direct) {
+ if (Details->Type == Branch::Break || Details->Type == Branch::Continue) {
assert(LoopStack.size() > 0);
if (Details->Ancestor != LoopStack.top() && Details->Labeled) {
LabeledShape *Labeled = Shape::IsLabeled(Details->Ancestor);
diff --git a/src/relooper/Relooper.h b/src/relooper/Relooper.h
index 85adf359..152bae0e 100644
--- a/src/relooper/Relooper.h
+++ b/src/relooper/Relooper.h
@@ -24,9 +24,11 @@ struct Shape;
// Info about a branching from one block to another
struct Branch {
enum FlowType {
- Direct = 0, // We will directly reach the right location through other means, no need for continue or break
+ Direct = 0, // We will directly reach the right location through other means, no need for continue or break
Break = 1,
- Continue = 2
+ Continue = 2,
+ Nested = 3 // This code is directly reached, but we must be careful to ensure it is nested in an if - it is not reached
+ // unconditionally, other code paths exist alongside it that we need to make sure do not intertwine
};
Shape *Ancestor; // If not NULL, this shape is the relevant one for purposes of getting to the target block. We break or continue on it
Branch::FlowType Type; // If Ancestor is not NULL, this says whether to break or continue
@@ -59,7 +61,7 @@ struct Block {
Shape *Parent; // The shape we are directly inside
int Id; // A unique identifier, defined when added to relooper. Note that this uniquely identifies a *logical* block - if we split it, the two instances have the same content *and* the same Id
const char *Code; // The string representation of the code in this block. Owning pointer (we copy the input)
- const char *BranchVar; // If we have more than one branch out, the variable whose value determines where we go
+ const char *BranchVar; // A variable whose value determines where we go; if this is not NULL, emit a switch on that variable
bool IsCheckedMultipleEntry; // If true, we are a multiple entry, so reaching us requires setting the label variable
Block(const char *CodeInit, const char *BranchVarInit);
diff --git a/src/relooper/test.cpp b/src/relooper/test.cpp
index b4ce669c..9f3ddceb 100644
--- a/src/relooper/test.cpp
+++ b/src/relooper/test.cpp
@@ -312,7 +312,128 @@ int main() {
printf("\n\n", "the_var");
r.Render();
- puts(buffer);
+ puts(r.GetOutputBuffer());
+ }
+
+ if (1) {
+ Relooper::MakeOutputBuffer(10);
+
+ printf("\n\n-- If chain (optimized) --\n\n");
+
+ Block *b_a = new Block("// block A\n", NULL);
+ Block *b_b = new Block("// block B\n", NULL);
+ Block *b_c = new Block("// block C\n", NULL);
+
+ b_a->AddBranchTo(b_b, "a == 10", NULL);
+ b_a->AddBranchTo(b_c, NULL, NULL);
+
+ b_b->AddBranchTo(b_c, NULL, NULL);
+
+ Relooper r;
+ r.AddBlock(b_a);
+ r.AddBlock(b_b);
+ r.AddBlock(b_c);
+
+ r.Calculate(b_a);
+ r.Render();
+
+ puts(r.GetOutputBuffer());
+ }
+
+ if (1) {
+ Relooper::MakeOutputBuffer(10);
+
+ printf("\n\n-- If chain (optimized) --\n\n");
+
+ Block *b_a = new Block("// block A\n", NULL);
+ Block *b_b = new Block("// block B\n", NULL);
+ Block *b_c = new Block("// block C\n", NULL);
+ Block *b_d = new Block("// block D\n", NULL);
+
+ b_a->AddBranchTo(b_b, "a == 10", NULL);
+ b_a->AddBranchTo(b_d, NULL, NULL);
+
+ b_b->AddBranchTo(b_c, "b == 10", NULL);
+ b_b->AddBranchTo(b_d, NULL, NULL);
+
+ b_c->AddBranchTo(b_d, NULL, NULL);
+
+ Relooper r;
+ r.AddBlock(b_a);
+ r.AddBlock(b_b);
+ r.AddBlock(b_c);
+ r.AddBlock(b_d);
+
+ r.Calculate(b_a);
+ r.Render();
+
+ puts(r.GetOutputBuffer());
+ }
+
+ if (1) {
+ Relooper::MakeOutputBuffer(10);
+
+ printf("\n\n-- If chain (optimized, long) --\n\n");
+
+ Block *b_a = new Block("// block A\n", NULL);
+ Block *b_b = new Block("// block B\n", NULL);
+ Block *b_c = new Block("// block C\n", NULL);
+ Block *b_d = new Block("// block D\n", NULL);
+ Block *b_e = new Block("// block E\n", NULL);
+
+ b_a->AddBranchTo(b_b, "a == 10", NULL);
+ b_a->AddBranchTo(b_e, NULL, NULL);
+
+ b_b->AddBranchTo(b_c, "b == 10", NULL);
+ b_b->AddBranchTo(b_e, NULL, NULL);
+
+ b_c->AddBranchTo(b_d, "c == 10", NULL);
+ b_c->AddBranchTo(b_e, NULL, NULL);
+
+ b_d->AddBranchTo(b_e, NULL, NULL);
+
+ Relooper r;
+ r.AddBlock(b_a);
+ r.AddBlock(b_b);
+ r.AddBlock(b_c);
+ r.AddBlock(b_d);
+ r.AddBlock(b_e);
+
+ r.Calculate(b_a);
+ r.Render();
+
+ puts(r.GetOutputBuffer());
+ }
+
+ if (1) {
+ Relooper::MakeOutputBuffer(10);
+
+ printf("\n\n-- If chain (optimized, lead to complex) --\n\n");
+
+ Block *b_a = new Block("// block A\n", NULL);
+ Block *b_b = new Block("// block B\n", NULL);
+ Block *b_c = new Block("// block C\n", NULL);
+ Block *b_d = new Block("// block D\n", NULL);
+
+ b_a->AddBranchTo(b_b, "a == 10", NULL);
+ b_a->AddBranchTo(b_d, NULL, NULL);
+
+ b_b->AddBranchTo(b_c, "b == 10", NULL);
+ b_b->AddBranchTo(b_d, NULL, NULL);
+
+ b_c->AddBranchTo(b_c, "loop", NULL);
+ b_c->AddBranchTo(b_d, NULL, NULL);
+
+ Relooper r;
+ r.AddBlock(b_a);
+ r.AddBlock(b_b);
+ r.AddBlock(b_c);
+ r.AddBlock(b_d);
+
+ r.Calculate(b_a);
+ r.Render();
+
+ puts(r.GetOutputBuffer());
}
}
diff --git a/src/relooper/test.txt b/src/relooper/test.txt
index 82b02ad7..d53aeeb1 100644
--- a/src/relooper/test.txt
+++ b/src/relooper/test.txt
@@ -324,10 +324,6 @@
label = 1;
L0: while(1) {
switch(label|0) {
- case 3: {
- // block C
- break;
- }
case 1: {
// block A
if (check == 10) {
@@ -357,6 +353,67 @@
}
break;
}
+ case 3: {
+ // block C
+ break;
+ }
+ }
+ }
+
+
+
+-- If chain (optimized) --
+
+ // block A
+ if (a == 10) {
+ // block B
+ }
+ // block C
+
+
+
+-- If chain (optimized) --
+
+ // block A
+ if (a == 10) {
+ // block B
+ if (b == 10) {
+ // block C
+ }
+ }
+ // block D
+
+
+
+-- If chain (optimized, long) --
+
+ // block A
+ if (a == 10) {
+ // block B
+ if (b == 10) {
+ // block C
+ if (c == 10) {
+ // block D
+ }
+ }
+ }
+ // block E
+
+
+
+-- If chain (optimized, lead to complex) --
+
+ // block A
+ if (a == 10) {
+ // block B
+ if (b == 10) {
+ while(1) {
+ // block C
+ if (!(loop)) {
+ break;
+ }
+ }
}
}
+ // block D
diff --git a/src/runtime.js b/src/runtime.js
index 97de7473..2ae68279 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -484,6 +484,11 @@ var Runtime = {
return ret;
}
this.processJSString = function processJSString(string) {
+ /* TODO: use TextEncoder when present,
+ var encoder = new TextEncoder();
+ encoder['encoding'] = "utf-8";
+ var utf8Array = encoder['encode'](aMsg.data);
+ */
string = unescape(encodeURIComponent(string));
var ret = [];
for (var i = 0; i < string.length; i++) {
diff --git a/src/settings.js b/src/settings.js
index 02f6c8b5..8b046e95 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -133,6 +133,7 @@ var PRECISE_F32 = 0; // 0: Use JS numbers for floating-point values. These are 6
// therefore recommended.
var SIMD = 0; // Whether to emit SIMD code ( https://github.com/johnmccutchan/ecmascript_simd )
+var CLOSURE_COMPILER = 0; // Whether closure compiling is being run on this output
var CLOSURE_ANNOTATIONS = 0; // If set, the generated code will be annotated for the closure
// compiler. This potentially lets closure optimize the code better.
@@ -164,8 +165,14 @@ var OUTLINING_LIMIT = 0; // A function size above which we try to automatically
// throughput. It is hard to say what values to start testing
// with, but something around 20,000 to 100,000 might make sense.
// (The unit size is number of AST nodes.)
+ // Outlining decreases maximum function size, but does so at the
+ // cost of increasing overall code size as well as performance
+ // (outlining itself makes code less optimized, and requires
+ // emscripten to disable some passes that are incompatible with
+ // it).
var AGGRESSIVE_VARIABLE_ELIMINATION = 0; // Run aggressiveVariableElimination in js-optimizer.js
+var SIMPLIFY_IFS = 1; // Whether to simplify ifs in js-optimizer.js
// Generated code debugging options
var SAFE_HEAP = 0; // Check each write to the heap, for example, this will give a clear
@@ -224,6 +231,19 @@ var LIBRARY_DEBUG = 0; // Print out when we enter a library call (library*.js).
var SOCKET_DEBUG = 0; // Log out socket/network data transfer.
var SOCKET_WEBRTC = 0; // Select socket backend, either webrtc or websockets.
+// As well as being configurable at compile time via the "-s" option the WEBSOCKET_URL and WEBSOCKET_SUBPROTOCOL
+// settings may configured at run time via the Module object e.g.
+// Module['websocket'] = {subprotocol: 'base64, binary, text'};
+// Module['websocket'] = {url: 'wss://', subprotocol: 'base64'};
+// Run time configuration may be useful as it lets an application select multiple different services.
+var WEBSOCKET_URL = 'ws://'; // A string containing either a WebSocket URL prefix (ws:// or wss://) or a complete
+ // RFC 6455 URL - "ws[s]:" "//" host [ ":" port ] path [ "?" query ].
+ // In the (default) case of only a prefix being specified the URL will be constructed from
+ // prefix + addr + ':' + port
+ // where addr and port are derived from the socket connect/bind/accept calls.
+var WEBSOCKET_SUBPROTOCOL = 'binary'; // A string containing a comma separated list of WebSocket subprotocols
+ // as would be present in the Sec-WebSocket-Protocol header.
+
var OPENAL_DEBUG = 0; // Print out debugging information from our OpenAL implementation.
var GL_ASSERTIONS = 0; // Adds extra checks for error situations in the GL library. Can impact performance.
diff --git a/src/shell.js b/src/shell.js
index 65f7b923..e1c0eb54 100644
--- a/src/shell.js
+++ b/src/shell.js
@@ -14,7 +14,11 @@
// before the code. Then that object will be used in the code, and you
// can continue to use Module afterwards as well.
var Module;
+#if CLOSURE_COMPILER
if (!Module) Module = eval('(function() { try { return {{{ EXPORT_NAME }}} || {} } catch(e) { return {} } })()');
+#else
+if (!Module) Module = (typeof {{{ EXPORT_NAME }}} !== 'undefined' ? {{{ EXPORT_NAME }}} : null) || {};
+#endif
// Sometimes an existing Module object exists with properties
// meant to overwrite the default module functionality. Here
diff --git a/system/include/emscripten/emscripten.h b/system/include/emscripten/emscripten.h
index 4c1f7ad7..2b883f93 100644
--- a/system/include/emscripten/emscripten.h
+++ b/system/include/emscripten/emscripten.h
@@ -96,7 +96,7 @@ extern void emscripten_async_run_script(const char *script, int millis);
* for this is to load an asset module, that is, the output of the
* file packager.
*/
-extern void emscripten_async_load_script(const char *script, void (*onload)(), void (*onerror)());
+extern void emscripten_async_load_script(const char *script, void (*onload)(void), void (*onerror)(void));
/*
* Set a C function as the main event loop. The JS environment
@@ -138,11 +138,11 @@ extern void emscripten_async_load_script(const char *script, void (*onload)(), v
* before the main loop will be called the first time.
*/
#if __EMSCRIPTEN__
-extern void emscripten_set_main_loop(void (*func)(), int fps, int simulate_infinite_loop);
+extern void emscripten_set_main_loop(void (*func)(void), int fps, int simulate_infinite_loop);
extern void emscripten_set_main_loop_arg(void (*func)(void*), void *arg, int fps, int simulate_infinite_loop);
-extern void emscripten_pause_main_loop();
-extern void emscripten_resume_main_loop();
-extern void emscripten_cancel_main_loop();
+extern void emscripten_pause_main_loop(void);
+extern void emscripten_resume_main_loop(void);
+extern void emscripten_cancel_main_loop(void);
#else
#define emscripten_set_main_loop(func, fps, simulateInfiniteLoop) \
while (1) { func(); usleep(1000000/fps); }
@@ -214,7 +214,7 @@ inline void emscripten_async_call(void (*func)(void *), void *arg, int millis) {
* etc. are not run). This is implicitly performed when you do
* an asynchronous operation like emscripten_async_call.
*/
-extern void emscripten_exit_with_live_runtime();
+extern void emscripten_exit_with_live_runtime(void);
/*
* Hide the OS mouse cursor over the canvas. Note that SDL's
@@ -222,7 +222,7 @@ extern void emscripten_exit_with_live_runtime();
* the OS one. This command is useful to hide the OS cursor
* if your app draws its own cursor.
*/
-void emscripten_hide_mouse();
+void emscripten_hide_mouse(void);
/*
* Resizes the pixel width and height of the <canvas> element
@@ -244,10 +244,10 @@ void emscripten_get_canvas_size(int *width, int *height, int *isFullscreen);
* other calls to this function. The unit is ms.
*/
#if __EMSCRIPTEN__
-double emscripten_get_now();
+double emscripten_get_now(void);
#else
#include <time.h>
-double emscripten_get_now() {
+double emscripten_get_now(void) {
return (1000*clock())/(double)CLOCKS_PER_SEC;
}
#endif
@@ -255,7 +255,7 @@ double emscripten_get_now() {
/*
* Simple random number generation in [0, 1), maps to Math.random().
*/
-float emscripten_random();
+float emscripten_random(void);
/*
* This macro-looking function will cause Emscripten to
@@ -323,6 +323,28 @@ void emscripten_async_wget_data(const char* url, void *arg, void (*onload)(void*
void emscripten_async_wget2(const char* url, const char* file, const char* requesttype, const char* param, void *arg, void (*onload)(void*, const char*), void (*onerror)(void*, int), void (*onprogress)(void*, int));
/*
+ * More feature-complete version of emscripten_async_wget_data. Note:
+ * this version is experimental.
+ *
+ * The requesttype is 'GET' or 'POST',
+ * If is post request, param is the post parameter
+ * like key=value&key2=value2.
+ * The param 'arg' is a pointer will be pass to the callback
+ * The free param tells the runtime whether to free the returned buffer
+ after onload is complete. If false freeing the buffer is the receiver's
+ responsibility.
+ * The callbacks are called with an object pointer give in parameter.
+ * When file is ready then 'onload' callback will called with a pointer to
+ the buffer in memory and the size in bytes.
+ * During the download 'onprogress' callback will called. The first argument is
+ the number of bytes loaded. The second argument is the total size in bytes,
+ or zero if the size is unavailable.
+ * If any error occurred 'onerror' will called with the HTTP status code
+ and a string with the status description.
+ */
+void emscripten_async_wget2_data(const char* url, const char* requesttype, const char* param, void *arg, int free, void (*onload)(void*, void*, unsigned), void (*onerror)(void*, int, const char*), void (*onprogress)(void*, int, int));
+
+/*
* Prepare a file in asynchronous way. This does just the
* preparation part of emscripten_async_wget, that is, it
* works on file data already present, and asynchronously
diff --git a/system/include/emscripten/html5.h b/system/include/emscripten/html5.h
index 6109d87f..db81725a 100644
--- a/system/include/emscripten/html5.h
+++ b/system/include/emscripten/html5.h
@@ -123,6 +123,10 @@ extern "C" {
/*
* The event structure passed in keyboard events keypress, keydown and keyup.
* https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#keys
+ *
+ * Note that since the DOM Level 3 Events spec is very recent at the time of writing (2014-03), uniform
+ * support for the different fields in the spec is still in flux. Be sure to check the results in multiple
+ * browsers. See the unmerged pull request #2222 for an example way on how to interpret the legacy key events.
*/
typedef struct EmscriptenKeyboardEvent {
// The printed representation of the pressed key.
@@ -144,14 +148,18 @@ typedef struct EmscriptenKeyboardEvent {
EM_UTF8 locale[32];
// The following fields are values from previous versions of the DOM key events specifications.
// See https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent?redirectlocale=en-US&redirectslug=DOM%2FKeyboardEvent
- // The character representation of the key.
+ // The character representation of the key. This is the field 'char' from the docs, but renamed to charValue to avoid a C reserved word.
+ // Warning: This attribute has been dropped from DOM Level 3 events.
EM_UTF8 charValue[32];
// The Unicode reference number of the key; this attribute is used only by the keypress event. For keys whose char attribute
// contains multiple characters, this is the Unicode value of the first character in that attribute.
+ // Warning: This attribute is deprecated, you should use the field 'key' instead, if available.
unsigned long charCode;
// A system and implementation dependent numerical code identifying the unmodified value of the pressed key.
+ // Warning: This attribute is deprecated, you should use the field 'key' instead, if available.
unsigned long keyCode;
// A system and implementation dependent numeric code identifying the unmodified value of the pressed key; this is usually the same as keyCode.
+ // Warning: This attribute is deprecated, you should use the field 'key' instead, if available.
unsigned long which;
} EmscriptenKeyboardEvent;
@@ -376,7 +384,7 @@ extern EMSCRIPTEN_RESULT emscripten_lock_orientation(int allowedOrientations);
/*
* Allows the screen to turn again into any orientation.
*/
-extern EMSCRIPTEN_RESULT emscripten_unlock_orientation();
+extern EMSCRIPTEN_RESULT emscripten_unlock_orientation(void);
/*
* The event structure passed in the fullscreenchange event.
@@ -420,7 +428,7 @@ extern EMSCRIPTEN_RESULT emscripten_request_fullscreen(const char *target, int d
/*
* Returns back to windowed browsing mode.
*/
-extern EMSCRIPTEN_RESULT emscripten_exit_fullscreen();
+extern EMSCRIPTEN_RESULT emscripten_exit_fullscreen(void);
/*
* The event structure passed in the pointerlockchange event.
@@ -456,7 +464,7 @@ extern EMSCRIPTEN_RESULT emscripten_request_pointerlock(const char *target, int
/*
* Exits pointer lock state and restores the mouse cursor to be visible again.
*/
-extern EMSCRIPTEN_RESULT emscripten_exit_pointerlock();
+extern EMSCRIPTEN_RESULT emscripten_exit_pointerlock(void);
#define EMSCRIPTEN_VISIBILITY_HIDDEN 0
#define EMSCRIPTEN_VISIBILITY_VISIBLE 1
@@ -573,7 +581,7 @@ extern EMSCRIPTEN_RESULT emscripten_set_gamepaddisconnected_callback(void *userD
* Returns the number of gamepads connected to the system or EMSCRIPTEN_RESULT_NOT_SUPPORTED if the current browser does not support gamepads.
* Note: A gamepad does not show up as connected until a button on it is pressed.
*/
-extern int emscripten_get_num_gamepads();
+extern int emscripten_get_num_gamepads(void);
/*
* Returns a snapshot of the current gamepad state.
*/
diff --git a/tests/cases/aliases_fastcomp.ll b/tests/cases/aliases_fastcomp.ll
new file mode 100644
index 00000000..2b8746eb
--- /dev/null
+++ b/tests/cases/aliases_fastcomp.ll
@@ -0,0 +1,41 @@
+; ModuleID = 'tests/hello_world.bc'
+target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:128-n32-S128"
+target triple = "asmjs-unknown-emscripten"
+
+@.str = private unnamed_addr constant [18 x i8] c"hello, world! %d\0A\00", align 1 ; [#uses=1 type=[18 x i8]*]
+
+@othername = alias internal void (i32)* @doit
+@othername2 = alias internal void (i32)* @othername
+@othername3 = alias internal void (i32)* @othername2
+
+@value = global i32 17
+@value2 = alias i32* @value
+@value3 = alias i32* @value
+
+define internal void @doit(i32 %x) {
+ %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([18 x i8]* @.str, i32 0, i32 0), i32 %x) ; [#uses=0 type=i32]
+ ret void
+}
+
+define i32 @main() {
+entry:
+ %fp = ptrtoint void (i32)* @othername3 to i32
+ %fp1 = add i32 %fp, 0
+ %pf = inttoptr i32 %fp1 to void (i32)*
+ %x = load i32* @value3
+ call void (i32)* %pf(i32 %x)
+ %x1 = load i32* @value2
+ call void (i32)* @othername3(i32 %x1)
+ %x2 = load i32* @value
+ call void (i32)* @othername2(i32 %x2)
+ store i32 18, i32* @value
+ %x3 = load i32* @value
+ call void (i32)* @othername(i32 %x3)
+ store i32 19, i32* @value3
+ %x4 = load i32* @value3
+ call void (i32)* @doit(i32 %x4)
+ ret i32 1
+}
+
+declare i32 @printf(i8*, ...)
+
diff --git a/tests/cases/aliases_fastcomp.txt b/tests/cases/aliases_fastcomp.txt
new file mode 100644
index 00000000..ae30c63c
--- /dev/null
+++ b/tests/cases/aliases_fastcomp.txt
@@ -0,0 +1,5 @@
+hello, world! 17
+hello, world! 17
+hello, world! 17
+hello, world! 18
+hello, world! 19
diff --git a/tests/emscripten_log/emscripten_log.cpp b/tests/emscripten_log/emscripten_log.cpp
index 5973e94c..f4cc41db 100644
--- a/tests/emscripten_log/emscripten_log.cpp
+++ b/tests/emscripten_log/emscripten_log.cpp
@@ -5,10 +5,6 @@
#define STRINGIZE_HELPER(x) #x
#define STRINGIZE(x) STRINGIZE_HELPER(x)
-#ifndef REPORT_RESULT
-#define REPORT_RESULT int dummy
-#endif
-
int result = 1; // If 1, this test succeeded.
// A custom assert macro to test varargs routing to emscripten_log().
@@ -134,11 +130,10 @@ void __attribute__((noinline)) Foo() // Arbitrary function signature to add some
int main()
{
Foo<int>();
-#ifndef RUN_FROM_JS_SHELL
+#ifdef REPORT_RESULT
REPORT_RESULT();
- return 0;
-#else
+#endif
if (result)
printf("Success!\n");
-#endif
+ return 0;
}
diff --git a/tests/fuzz/csmith_driver.py b/tests/fuzz/csmith_driver.py
index 238acb9a..09f1c249 100755
--- a/tests/fuzz/csmith_driver.py
+++ b/tests/fuzz/csmith_driver.py
@@ -121,7 +121,7 @@ while 1:
print "EMSCRIPTEN BUG"
notes['embug'] += 1
fails += 1
- shutil.copyfile(fullname, 'newfail%d%s' % (fails, suffix))
+ shutil.copyfile(fullname, 'newfail%d%s%s' % (fails, opts.replace('-', '_'), suffix))
continue
#if not ok:
# try: # finally, try with safe heap. if that is triggered, this is nonportable code almost certainly
@@ -138,7 +138,7 @@ while 1:
# This is ok. Try in secondary JS engine too
if opts != '-O0' and engine2 and normal:
try:
- js2 = shared.run_js(filename + '.js', stderr=PIPE, engine=engine2, full_output=True, check_timeout=True)
+ js2 = shared.run_js(filename + '.js', stderr=PIPE, engine=engine2 + ['-w'], full_output=True, check_timeout=True)
except:
print 'failed to run in secondary', js2
break
diff --git a/tests/mem_init.cpp b/tests/mem_init.cpp
new file mode 100644
index 00000000..e642bfc9
--- /dev/null
+++ b/tests/mem_init.cpp
@@ -0,0 +1,24 @@
+#include <stdio.h>
+#include <emscripten.h>
+
+extern "C" {
+
+int noted = 0;
+
+void EMSCRIPTEN_KEEPALIVE note(int n) {
+ EM_ASM_({ Module.print([$0, $1]) }, n, noted);
+ noted = noted | n;
+ EM_ASM_({ Module.print(['noted is now', $0]) }, noted);
+ if (noted == 3) {
+ int result = noted;
+ REPORT_RESULT();
+ }
+}
+
+}
+
+int main() {
+ EM_ASM( myJSCallback() ); // calls a global JS func
+ return 0;
+}
+
diff --git a/tests/openal_playback.cpp b/tests/openal_playback.cpp
index 880b6906..46c4f8a3 100644
--- a/tests/openal_playback.cpp
+++ b/tests/openal_playback.cpp
@@ -25,7 +25,7 @@ void playSource(void* arg)
alGetSourcei(source, AL_SOURCE_STATE, &state);
assert(state == AL_STOPPED);
-#ifdef __EMSCRIPTEN__
+#ifdef REPORT_RESULT
int result = 1;
REPORT_RESULT();
#endif
diff --git a/tests/runner.py b/tests/runner.py
index c13a16eb..26912f25 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -779,14 +779,11 @@ A recommended order is:
(the main test suite)
other - tests separate from the main suite
browser - runs pages in a web browser
+ interactive - runs interactive browser tests that need human verification, and could not be automated
sockets - runs websocket networking tests
benchmark - run before and after each set of changes before pushing to
master, verify no regressions
-There are also commands to run specific subsets of the test suite:
-
- browser.audio - runs audio tests in a web browser (requires human verification)
-
To run one of those parts, do something like
python tests/runner.py sanity
diff --git a/tests/sdl_wm_togglefullscreen.c b/tests/sdl_wm_togglefullscreen.c
new file mode 100644
index 00000000..c76ced76
--- /dev/null
+++ b/tests/sdl_wm_togglefullscreen.c
@@ -0,0 +1,78 @@
+#include <stdio.h>
+#include <SDL/SDL.h>
+#include <SDL/SDL_ttf.h>
+#include <assert.h>
+#include <emscripten.h>
+
+int result = 1;
+
+SDL_Surface *screen = 0;
+
+int inFullscreen = 0;
+
+int wasFullscreen = 0;
+
+void render() {
+ int width, height, isfs;
+ emscripten_get_canvas_size(&width, &height, &isfs);
+ SDL_Rect rect = { 0, 0, width, height };
+ SDL_FillRect(screen, &rect, 0xff00ffff);
+}
+
+void mainloop() {
+ render();
+ SDL_Event event;
+ int isInFullscreen = EM_ASM_INT_V(return !!(document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement));
+ if (isInFullscreen && !wasFullscreen) {
+ printf("Successfully transitioned to fullscreen mode!\n");
+ wasFullscreen = isInFullscreen;
+ }
+
+ if (wasFullscreen && !isInFullscreen) {
+ printf("Exited fullscreen. Test succeeded.\n");
+ result = 1;
+#ifdef REPORT_RESULT
+ REPORT_RESULT();
+#endif
+ wasFullscreen = isInFullscreen;
+ emscripten_cancel_main_loop();
+ return;
+ }
+
+ int haveEvent = SDL_PollEvent(&event);
+ if (haveEvent) {
+ switch(event.type) {
+ case SDL_MOUSEBUTTONDOWN: {
+ SDL_WM_ToggleFullScreen(screen);
+ inFullscreen = 1 - inFullscreen;
+ if (inFullscreen == 0) {
+ result = wasFullscreen;
+ if (result) {
+ printf("Exited fullscreen. Test succeeded.\n");
+ } else {
+ printf("Exited fullscreen. Test failed, fullscreen transition did not happen!\n");
+ }
+#ifdef REPORT_RESULT
+ REPORT_RESULT();
+#endif
+ emscripten_cancel_main_loop();
+ return;
+ } else {
+ printf("Entering fullscreen...\n");
+ }
+ break;
+ }
+ }
+ }
+}
+
+int main() {
+ SDL_Init(SDL_INIT_VIDEO);
+ screen = SDL_SetVideoMode(600, 450, 32, SDL_HWSURFACE);
+
+ printf("You should see a yellow canvas.\n");
+ printf("Click on the canvas to enter full screen, and then click on the canvas again to finish the test.\n");
+ printf("When in full screen, you should see the whole screen filled yellow, and after exiting, the yellow canvas should be restored in the window.\n");
+ emscripten_set_main_loop(mainloop, 0, 0);
+ return 0;
+}
diff --git a/tests/sockets/p2p/.gitignore b/tests/sockets/p2p/.gitignore
new file mode 100644
index 00000000..065a4ac0
--- /dev/null
+++ b/tests/sockets/p2p/.gitignore
@@ -0,0 +1,2 @@
+node_modules
+ssl
diff --git a/tests/sockets/p2p/broker/p2p-broker.js b/tests/sockets/p2p/broker/p2p-broker.js
new file mode 100644
index 00000000..028eb25b
--- /dev/null
+++ b/tests/sockets/p2p/broker/p2p-broker.js
@@ -0,0 +1,231 @@
+var crypto = require('crypto');
+var fs = require('fs');
+var https = require('https');
+
+var SSL_KEY = 'ssl/ssl.key';
+var SSL_CERT = 'ssl/ssl-unified.crt';
+var PORT = 8080;
+
+var sslSupported = false;
+if(fs.existsSync(SSL_KEY) && fs.existsSync(SSL_CERT) && fs.statSync(SSL_KEY).isFile() && fs.statSync(SSL_CERT).isFile()) {
+ sslSupported = true;
+}
+
+function handler(req, res) {
+ res.writeHead(200);
+ res.end("p2p");
+};
+
+var app, port;
+if(sslSupported) {
+ var sslopts = {
+ key: fs.readFileSync(SSL_KEY),
+ cert: fs.readFileSync(SSL_CERT)
+ };
+ sslopts.agent = new https.Agent(sslopts);
+ app = require('https').createServer(sslopts, handler);
+ port = 8081;
+ console.info('ssl mode enabled');
+} else {
+ app = require('http').createServer(handler);
+ port = 8080;
+ console.info('ssl mode disabled');
+}
+console.info('listening on port', port);
+
+var io = require('socket.io').listen(app, {
+ 'log level': 2
+});
+
+app.listen(port);
+
+var jsMime = {
+ type: 'application/javascript',
+ encoding: 'utf8',
+ gzip: true
+};
+
+io.static.add('/p2p-client.js', {
+ mime: jsMime,
+ file: 'client/p2p-client.js'
+});
+
+/*
+io.static.add('/p2p-client.min.js', {
+ mime: jsMime,
+ file: 'client/p2p-client.min.js'
+});
+*/
+
+function mkguid() {
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
+ var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
+ return v.toString(16);
+ }).toUpperCase();
+};
+
+var peers = {};
+var hosts = {};
+
+var E = {
+ OK: 'ok'
+ , NOROUTE: 'no such route'
+ , ISNOTHOST: 'peer is not a host'
+};
+
+function Peer(socket) {
+ this.socket = socket;
+ this.host = null;
+};
+
+function Host(options) {
+ this.route = options['route'];
+ this.url = options['url'];
+ this.listed = (undefined !== options['listed']) ? options['listed'] : false;
+ this.metadata = options['metadata'] || {};
+ this.ctime = Date.now();
+ this.mtime = Date.now();
+};
+Host.prototype.update = function update(options) {
+ this.url = options['url'];
+ this.listed = (undefined !== options['listed']) ? options['listed'] : false;
+ this.metadata = options['metadata'] || {};
+ this.mtime = Date.now();
+};
+
+io.of('/peer').on('connection', function(socket) {
+ var route = crypto.createHash('md5').update(socket['id']).digest('hex');
+ socket.emit('route', route);
+
+ socket.on('disconnect', function() {
+ if(hosts[route]) {
+ var host = hosts[route];
+ changeList('remove', host);
+ }
+ delete hosts[route];
+ delete peers[route];
+ });
+
+ socket.on('send', function(message, callback) {
+ var to = message['to'];
+
+ if(!peers.hasOwnProperty(to)) {
+ callback({'error': E.NOROUTE});
+ return;
+ }
+
+ var from = route;
+ var data = message['data'];
+ peers[to].emit('receive', {
+ 'from': from,
+ 'data': data
+ });
+ });
+
+ socket.on('listen', function(options, callback) {
+ options['route'] = route;
+ if(hosts.hasOwnProperty(route)) {
+ hosts[route].update(options);
+ changeList('update', hosts[route]);
+ } else {
+ hosts[route] = new Host(options);
+ changeList('append', hosts[route]);
+ }
+
+ callback();
+ });
+
+ socket.on('ignore', function(message, callback) {
+ if(!hosts.hasOwnProperty(route)) {
+ callback({'error': E.ISNOTHOST});
+ return;
+ }
+
+ var host = hosts[route];
+ delete hosts[route];
+
+ changeList('remove', host);
+
+ callback();
+ });
+
+ peers[route] = socket;
+});
+
+function Filter(socket, options) {
+ this.options = options || {};
+ this.socket = socket;
+};
+Filter.prototype.test = function test(host) {
+ var filter = this.options;
+ var result;
+
+ if(filter['url'] && typeof host['url'] === 'string') {
+ try {
+ result = host['url'].match(filter['url']);
+ if(!result)
+ return true;
+ } catch(e) {
+ return true;
+ }
+ }
+
+ if(filter['metadata'] && host['metadata']) {
+ var metadataFilter = filter['metadata'];
+ var metadataHost = host['metadata'];
+
+ if(metadataFilter['name'] && typeof metadataHost['name'] === 'string') {
+ try {
+ result = metadataHost['name'].match(metadataFilter['name']);
+ if(!result)
+ return true;
+ } catch(e) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+};
+
+var lists = {};
+
+function changeList(operation, host) {
+ var clients = Object.keys(lists);
+ clients.forEach(function(client) {
+ var filter = lists[client];
+ if(!host['listed'])
+ return;
+ if(!filter.test(host)) {
+ var data = operation === 'remove' ? host['route'] : host;
+ filter.socket.emit(operation, data);
+ }
+ });
+};
+
+io.of('/list').on('connection', function(socket) {
+ var id = socket['id'];
+
+ socket.on('disconnect', function() {
+ delete lists[id];
+ });
+
+ socket.on('list', function(options) {
+ var filter = new Filter(socket, options);
+
+ var result = [];
+
+ var hostIds = Object.keys(hosts);
+ hostIds.forEach(function(hostId) {
+ var host = hosts[hostId];
+ if(!host['listed'])
+ return;
+ if(!filter.test(host))
+ result.push(host);
+ });
+
+ lists[id] = filter;
+
+ socket.emit('truncate', result);
+ });
+});
diff --git a/tests/sockets/p2p/client/p2p-client.js b/tests/sockets/p2p/client/p2p-client.js
new file mode 100644
index 00000000..2c660210
--- /dev/null
+++ b/tests/sockets/p2p/client/p2p-client.js
@@ -0,0 +1,4485 @@
+
+;(function(define, global) { 'use strict';
+define(['module'], function(module) {
+
+ /*! Socket.IO.js build:0.9.11, development. Copyright(c) 2011 LearnBoost <dev@learnboost.com> MIT Licensed
+ Modified to work in-line; Removed Flash transport code */
+ var io = ('undefined' === typeof module ? {} : module.exports);
+ (function() {
+
+ /**
+ * socket.io
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+ (function (exports, global) {
+
+ /**
+ * IO namespace.
+ *
+ * @namespace
+ */
+
+ var io = exports;
+
+ /**
+ * Socket.IO version
+ *
+ * @api public
+ */
+
+ io.version = '0.9.11';
+
+ /**
+ * Protocol implemented.
+ *
+ * @api public
+ */
+
+ io.protocol = 1;
+
+ /**
+ * Available transports, these will be populated with the available transports
+ *
+ * @api public
+ */
+
+ io.transports = [];
+
+ /**
+ * Keep track of jsonp callbacks.
+ *
+ * @api private
+ */
+
+ io.j = [];
+
+ /**
+ * Keep track of our io.Sockets
+ *
+ * @api private
+ */
+ io.sockets = {};
+
+
+ /**
+ * Manages connections to hosts.
+ *
+ * @param {String} uri
+ * @Param {Boolean} force creation of new socket (defaults to false)
+ * @api public
+ */
+
+ io.connect = function (host, details) {
+ var uri = io.util.parseUri(host)
+ , uuri
+ , socket;
+
+ if (global && global.location) {
+ uri.protocol = uri.protocol || global.location.protocol.slice(0, -1);
+ uri.host = uri.host || (global.document
+ ? global.document.domain : global.location.hostname);
+ uri.port = uri.port || global.location.port;
+ }
+
+ uuri = io.util.uniqueUri(uri);
+
+ var options = {
+ host: uri.host
+ , secure: 'https' == uri.protocol
+ , port: uri.port || ('https' == uri.protocol ? 443 : 80)
+ , query: uri.query || ''
+ };
+
+ io.util.merge(options, details);
+
+ if (options['force new connection'] || !io.sockets[uuri]) {
+ socket = new io.Socket(options);
+ }
+
+ if (!options['force new connection'] && socket) {
+ io.sockets[uuri] = socket;
+ }
+
+ socket = socket || io.sockets[uuri];
+
+ // if path is different from '' or /
+ return socket.of(uri.path.length > 1 ? uri.path : '');
+ };
+
+ })('object' === typeof module ? module.exports : (io = {}), global);
+ /**
+ * socket.io
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+ (function (exports, global) {
+
+ /**
+ * Utilities namespace.
+ *
+ * @namespace
+ */
+
+ var util = exports.util = {};
+
+ /**
+ * Parses an URI
+ *
+ * @author Steven Levithan <stevenlevithan.com> (MIT license)
+ * @api public
+ */
+
+ var re = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;
+
+ var parts = ['source', 'protocol', 'authority', 'userInfo', 'user', 'password',
+ 'host', 'port', 'relative', 'path', 'directory', 'file', 'query',
+ 'anchor'];
+
+ util.parseUri = function (str) {
+ var m = re.exec(str || '')
+ , uri = {}
+ , i = 14;
+
+ while (i--) {
+ uri[parts[i]] = m[i] || '';
+ }
+
+ return uri;
+ };
+
+ /**
+ * Produces a unique url that identifies a Socket.IO connection.
+ *
+ * @param {Object} uri
+ * @api public
+ */
+
+ util.uniqueUri = function (uri) {
+ var protocol = uri.protocol
+ , host = uri.host
+ , port = uri.port;
+
+ if ('document' in global) {
+ host = host || document.domain;
+ port = port || (protocol == 'https'
+ && document.location.protocol !== 'https:' ? 443 : document.location.port);
+ } else {
+ host = host || 'localhost';
+
+ if (!port && protocol == 'https') {
+ port = 443;
+ }
+ }
+
+ return (protocol || 'http') + '://' + host + ':' + (port || 80);
+ };
+
+ /**
+ * Mergest 2 query strings in to once unique query string
+ *
+ * @param {String} base
+ * @param {String} addition
+ * @api public
+ */
+
+ util.query = function (base, addition) {
+ var query = util.chunkQuery(base || '')
+ , components = [];
+
+ util.merge(query, util.chunkQuery(addition || ''));
+ for (var part in query) {
+ if (query.hasOwnProperty(part)) {
+ components.push(part + '=' + query[part]);
+ }
+ }
+
+ return components.length ? '?' + components.join('&') : '';
+ };
+
+ /**
+ * Transforms a querystring in to an object
+ *
+ * @param {String} qs
+ * @api public
+ */
+
+ util.chunkQuery = function (qs) {
+ var query = {}
+ , params = qs.split('&')
+ , i = 0
+ , l = params.length
+ , kv;
+
+ for (; i < l; ++i) {
+ kv = params[i].split('=');
+ if (kv[0]) {
+ query[kv[0]] = kv[1];
+ }
+ }
+
+ return query;
+ };
+
+ /**
+ * Executes the given function when the page is loaded.
+ *
+ * io.util.load(function () { console.log('page loaded'); });
+ *
+ * @param {Function} fn
+ * @api public
+ */
+
+ var pageLoaded = false;
+
+ util.load = function (fn) {
+ if ('document' in global && document.readyState === 'complete' || pageLoaded) {
+ return fn();
+ }
+
+ util.on(global, 'load', fn, false);
+ };
+
+ /**
+ * Adds an event.
+ *
+ * @api private
+ */
+
+ util.on = function (element, event, fn, capture) {
+ if (element.attachEvent) {
+ element.attachEvent('on' + event, fn);
+ } else if (element.addEventListener) {
+ element.addEventListener(event, fn, capture);
+ }
+ };
+
+ /**
+ * Generates the correct `XMLHttpRequest` for regular and cross domain requests.
+ *
+ * @param {Boolean} [xdomain] Create a request that can be used cross domain.
+ * @returns {XMLHttpRequest|false} If we can create a XMLHttpRequest.
+ * @api private
+ */
+
+ util.request = function (xdomain) {
+
+ if (xdomain && 'undefined' != typeof XDomainRequest && !util.ua.hasCORS) {
+ return new XDomainRequest();
+ }
+
+ if ('undefined' != typeof XMLHttpRequest && (!xdomain || util.ua.hasCORS)) {
+ return new XMLHttpRequest();
+ }
+
+ if (!xdomain) {
+ try {
+ return new window[(['Active'].concat('Object').join('X'))]('Microsoft.XMLHTTP');
+ } catch(e) { }
+ }
+
+ return null;
+ };
+
+ /**
+ * XHR based transport constructor.
+ *
+ * @constructor
+ * @api public
+ */
+
+ /**
+ * Change the internal pageLoaded value.
+ */
+
+ if ('undefined' != typeof window) {
+ util.load(function () {
+ pageLoaded = true;
+ });
+ }
+
+ /**
+ * Defers a function to ensure a spinner is not displayed by the browser
+ *
+ * @param {Function} fn
+ * @api public
+ */
+
+ util.defer = function (fn) {
+ if (!util.ua.webkit || 'undefined' != typeof importScripts) {
+ return fn();
+ }
+
+ util.load(function () {
+ setTimeout(fn, 100);
+ });
+ };
+
+ /**
+ * Merges two objects.
+ *
+ * @api public
+ */
+
+ util.merge = function merge (target, additional, deep, lastseen) {
+ var seen = lastseen || []
+ , depth = typeof deep == 'undefined' ? 2 : deep
+ , prop;
+
+ for (prop in additional) {
+ if (additional.hasOwnProperty(prop) && util.indexOf(seen, prop) < 0) {
+ if (typeof target[prop] !== 'object' || !depth) {
+ target[prop] = additional[prop];
+ seen.push(additional[prop]);
+ } else {
+ util.merge(target[prop], additional[prop], depth - 1, seen);
+ }
+ }
+ }
+
+ return target;
+ };
+
+ /**
+ * Merges prototypes from objects
+ *
+ * @api public
+ */
+
+ util.mixin = function (ctor, ctor2) {
+ util.merge(ctor.prototype, ctor2.prototype);
+ };
+
+ /**
+ * Shortcut for prototypical and static inheritance.
+ *
+ * @api private
+ */
+
+ util.inherit = function (ctor, ctor2) {
+ function f() {};
+ f.prototype = ctor2.prototype;
+ ctor.prototype = new f;
+ };
+
+ /**
+ * Checks if the given object is an Array.
+ *
+ * io.util.isArray([]); // true
+ * io.util.isArray({}); // false
+ *
+ * @param Object obj
+ * @api public
+ */
+
+ util.isArray = Array.isArray || function (obj) {
+ return Object.prototype.toString.call(obj) === '[object Array]';
+ };
+
+ /**
+ * Intersects values of two arrays into a third
+ *
+ * @api public
+ */
+
+ util.intersect = function (arr, arr2) {
+ var ret = []
+ , longest = arr.length > arr2.length ? arr : arr2
+ , shortest = arr.length > arr2.length ? arr2 : arr;
+
+ for (var i = 0, l = shortest.length; i < l; i++) {
+ if (~util.indexOf(longest, shortest[i]))
+ ret.push(shortest[i]);
+ }
+
+ return ret;
+ };
+
+ /**
+ * Array indexOf compatibility.
+ *
+ * @see bit.ly/a5Dxa2
+ * @api public
+ */
+
+ util.indexOf = function (arr, o, i) {
+
+ for (var j = arr.length, i = i < 0 ? i + j < 0 ? 0 : i + j : i || 0;
+ i < j && arr[i] !== o; i++) {}
+
+ return j <= i ? -1 : i;
+ };
+
+ /**
+ * Converts enumerables to array.
+ *
+ * @api public
+ */
+
+ util.toArray = function (enu) {
+ var arr = [];
+
+ for (var i = 0, l = enu.length; i < l; i++)
+ arr.push(enu[i]);
+
+ return arr;
+ };
+
+ /**
+ * UA / engines detection namespace.
+ *
+ * @namespace
+ */
+
+ util.ua = {};
+
+ /**
+ * Whether the UA supports CORS for XHR.
+ *
+ * @api public
+ */
+
+ util.ua.hasCORS = 'undefined' != typeof XMLHttpRequest && (function () {
+ try {
+ var a = new XMLHttpRequest();
+ } catch (e) {
+ return false;
+ }
+
+ return a.withCredentials != undefined;
+ })();
+
+ /**
+ * Detect webkit.
+ *
+ * @api public
+ */
+
+ util.ua.webkit = 'undefined' != typeof navigator
+ && /webkit/i.test(navigator.userAgent);
+
+ /**
+ * Detect iPad/iPhone/iPod.
+ *
+ * @api public
+ */
+
+ util.ua.iDevice = 'undefined' != typeof navigator
+ && /iPad|iPhone|iPod/i.test(navigator.userAgent);
+
+ })('undefined' != typeof io ? io : module.exports, global);
+ /**
+ * socket.io
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+ (function (exports, io) {
+
+ /**
+ * Expose constructor.
+ */
+
+ exports.EventEmitter = EventEmitter;
+
+ /**
+ * Event emitter constructor.
+ *
+ * @api public.
+ */
+
+ function EventEmitter () {};
+
+ /**
+ * Adds a listener
+ *
+ * @api public
+ */
+
+ EventEmitter.prototype.on = function (name, fn) {
+ if (!this.$events) {
+ this.$events = {};
+ }
+
+ if (!this.$events[name]) {
+ this.$events[name] = fn;
+ } else if (io.util.isArray(this.$events[name])) {
+ this.$events[name].push(fn);
+ } else {
+ this.$events[name] = [this.$events[name], fn];
+ }
+
+ return this;
+ };
+
+ EventEmitter.prototype.addListener = EventEmitter.prototype.on;
+
+ /**
+ * Adds a volatile listener.
+ *
+ * @api public
+ */
+
+ EventEmitter.prototype.once = function (name, fn) {
+ var self = this;
+
+ function on () {
+ self.removeListener(name, on);
+ fn.apply(this, arguments);
+ };
+
+ on.listener = fn;
+ this.on(name, on);
+
+ return this;
+ };
+
+ /**
+ * Removes a listener.
+ *
+ * @api public
+ */
+
+ EventEmitter.prototype.removeListener = function (name, fn) {
+ if (this.$events && this.$events[name]) {
+ var list = this.$events[name];
+
+ if (io.util.isArray(list)) {
+ var pos = -1;
+
+ for (var i = 0, l = list.length; i < l; i++) {
+ if (list[i] === fn || (list[i].listener && list[i].listener === fn)) {
+ pos = i;
+ break;
+ }
+ }
+
+ if (pos < 0) {
+ return this;
+ }
+
+ list.splice(pos, 1);
+
+ if (!list.length) {
+ delete this.$events[name];
+ }
+ } else if (list === fn || (list.listener && list.listener === fn)) {
+ delete this.$events[name];
+ }
+ }
+
+ return this;
+ };
+
+ /**
+ * Removes all listeners for an event.
+ *
+ * @api public
+ */
+
+ EventEmitter.prototype.removeAllListeners = function (name) {
+ if (name === undefined) {
+ this.$events = {};
+ return this;
+ }
+
+ if (this.$events && this.$events[name]) {
+ this.$events[name] = null;
+ }
+
+ return this;
+ };
+
+ /**
+ * Gets all listeners for a certain event.
+ *
+ * @api publci
+ */
+
+ EventEmitter.prototype.listeners = function (name) {
+ if (!this.$events) {
+ this.$events = {};
+ }
+
+ if (!this.$events[name]) {
+ this.$events[name] = [];
+ }
+
+ if (!io.util.isArray(this.$events[name])) {
+ this.$events[name] = [this.$events[name]];
+ }
+
+ return this.$events[name];
+ };
+
+ /**
+ * Emits an event.
+ *
+ * @api public
+ */
+
+ EventEmitter.prototype.emit = function (name) {
+ if (!this.$events) {
+ return false;
+ }
+
+ var handler = this.$events[name];
+
+ if (!handler) {
+ return false;
+ }
+
+ var args = Array.prototype.slice.call(arguments, 1);
+
+ if ('function' == typeof handler) {
+ handler.apply(this, args);
+ } else if (io.util.isArray(handler)) {
+ var listeners = handler.slice();
+
+ for (var i = 0, l = listeners.length; i < l; i++) {
+ listeners[i].apply(this, args);
+ }
+ } else {
+ return false;
+ }
+
+ return true;
+ };
+
+ })(
+ 'undefined' != typeof io ? io : module.exports
+ , 'undefined' != typeof io ? io : module.parent.exports
+ );
+
+ /**
+ * socket.io
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+ /**
+ * Based on JSON2 (http://www.JSON.org/js.html).
+ */
+
+ (function (exports, nativeJSON) {
+ "use strict";
+
+ // use native JSON if it's available
+ if (nativeJSON && nativeJSON.parse){
+ return exports.JSON = {
+ parse: nativeJSON.parse
+ , stringify: nativeJSON.stringify
+ };
+ }
+
+ var JSON = exports.JSON = {};
+
+ function f(n) {
+ // Format integers to have at least two digits.
+ return n < 10 ? '0' + n : n;
+ }
+
+ function date(d, key) {
+ return isFinite(d.valueOf()) ?
+ d.getUTCFullYear() + '-' +
+ f(d.getUTCMonth() + 1) + '-' +
+ f(d.getUTCDate()) + 'T' +
+ f(d.getUTCHours()) + ':' +
+ f(d.getUTCMinutes()) + ':' +
+ f(d.getUTCSeconds()) + 'Z' : null;
+ };
+
+ var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+ escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+ gap,
+ indent,
+ meta = { // table of character substitutions
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '"' : '\\"',
+ '\\': '\\\\'
+ },
+ rep;
+
+
+ function quote(string) {
+
+ // If the string contains no control characters, no quote characters, and no
+ // backslash characters, then we can safely slap some quotes around it.
+ // Otherwise we must also replace the offending characters with safe escape
+ // sequences.
+
+ escapable.lastIndex = 0;
+ return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
+ var c = meta[a];
+ return typeof c === 'string' ? c :
+ '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+ }) + '"' : '"' + string + '"';
+ }
+
+
+ function str(key, holder) {
+
+ // Produce a string from holder[key].
+
+ var i, // The loop counter.
+ k, // The member key.
+ v, // The member value.
+ length,
+ mind = gap,
+ partial,
+ value = holder[key];
+
+ // If the value has a toJSON method, call it to obtain a replacement value.
+
+ if (value instanceof Date) {
+ value = date(key);
+ }
+
+ // If we were called with a replacer function, then call the replacer to
+ // obtain a replacement value.
+
+ if (typeof rep === 'function') {
+ value = rep.call(holder, key, value);
+ }
+
+ // What happens next depends on the value's type.
+
+ switch (typeof value) {
+ case 'string':
+ return quote(value);
+
+ case 'number':
+
+ // JSON numbers must be finite. Encode non-finite numbers as null.
+
+ return isFinite(value) ? String(value) : 'null';
+
+ case 'boolean':
+ case 'null':
+
+ // If the value is a boolean or null, convert it to a string. Note:
+ // typeof null does not produce 'null'. The case is included here in
+ // the remote chance that this gets fixed someday.
+
+ return String(value);
+
+ // If the type is 'object', we might be dealing with an object or an array or
+ // null.
+
+ case 'object':
+
+ // Due to a specification blunder in ECMAScript, typeof null is 'object',
+ // so watch out for that case.
+
+ if (!value) {
+ return 'null';
+ }
+
+ // Make an array to hold the partial results of stringifying this object value.
+
+ gap += indent;
+ partial = [];
+
+ // Is the value an array?
+
+ if (Object.prototype.toString.apply(value) === '[object Array]') {
+
+ // The value is an array. Stringify every element. Use null as a placeholder
+ // for non-JSON values.
+
+ length = value.length;
+ for (i = 0; i < length; i += 1) {
+ partial[i] = str(i, value) || 'null';
+ }
+
+ // Join all of the elements together, separated with commas, and wrap them in
+ // brackets.
+
+ v = partial.length === 0 ? '[]' : gap ?
+ '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
+ '[' + partial.join(',') + ']';
+ gap = mind;
+ return v;
+ }
+
+ // If the replacer is an array, use it to select the members to be stringified.
+
+ if (rep && typeof rep === 'object') {
+ length = rep.length;
+ for (i = 0; i < length; i += 1) {
+ if (typeof rep[i] === 'string') {
+ k = rep[i];
+ v = str(k, value);
+ if (v) {
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
+ }
+ }
+ }
+ } else {
+
+ // Otherwise, iterate through all of the keys in the object.
+
+ for (k in value) {
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
+ v = str(k, value);
+ if (v) {
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
+ }
+ }
+ }
+ }
+
+ // Join all of the member texts together, separated with commas,
+ // and wrap them in braces.
+
+ v = partial.length === 0 ? '{}' : gap ?
+ '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
+ '{' + partial.join(',') + '}';
+ gap = mind;
+ return v;
+ }
+ }
+
+ // If the JSON object does not yet have a stringify method, give it one.
+
+ JSON.stringify = function (value, replacer, space) {
+
+ // The stringify method takes a value and an optional replacer, and an optional
+ // space parameter, and returns a JSON text. The replacer can be a function
+ // that can replace values, or an array of strings that will select the keys.
+ // A default replacer method can be provided. Use of the space parameter can
+ // produce text that is more easily readable.
+
+ var i;
+ gap = '';
+ indent = '';
+
+ // If the space parameter is a number, make an indent string containing that
+ // many spaces.
+
+ if (typeof space === 'number') {
+ for (i = 0; i < space; i += 1) {
+ indent += ' ';
+ }
+
+ // If the space parameter is a string, it will be used as the indent string.
+
+ } else if (typeof space === 'string') {
+ indent = space;
+ }
+
+ // If there is a replacer, it must be a function or an array.
+ // Otherwise, throw an error.
+
+ rep = replacer;
+ if (replacer && typeof replacer !== 'function' &&
+ (typeof replacer !== 'object' ||
+ typeof replacer.length !== 'number')) {
+ throw new Error('JSON.stringify');
+ }
+
+ // Make a fake root object containing our value under the key of ''.
+ // Return the result of stringifying the value.
+
+ return str('', {'': value});
+ };
+
+ // If the JSON object does not yet have a parse method, give it one.
+
+ JSON.parse = function (text, reviver) {
+ // The parse method takes a text and an optional reviver function, and returns
+ // a JavaScript value if the text is a valid JSON text.
+
+ var j;
+
+ function walk(holder, key) {
+
+ // The walk method is used to recursively walk the resulting structure so
+ // that modifications can be made.
+
+ var k, v, value = holder[key];
+ if (value && typeof value === 'object') {
+ for (k in value) {
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
+ v = walk(value, k);
+ if (v !== undefined) {
+ value[k] = v;
+ } else {
+ delete value[k];
+ }
+ }
+ }
+ }
+ return reviver.call(holder, key, value);
+ }
+
+
+ // Parsing happens in four stages. In the first stage, we replace certain
+ // Unicode characters with escape sequences. JavaScript handles many characters
+ // incorrectly, either silently deleting them, or treating them as line endings.
+
+ text = String(text);
+ cx.lastIndex = 0;
+ if (cx.test(text)) {
+ text = text.replace(cx, function (a) {
+ return '\\u' +
+ ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+ });
+ }
+
+ // In the second stage, we run the text against regular expressions that look
+ // for non-JSON patterns. We are especially concerned with '()' and 'new'
+ // because they can cause invocation, and '=' because it can cause mutation.
+ // But just to be safe, we want to reject all unexpected forms.
+
+ // We split the second stage into 4 regexp operations in order to work around
+ // crippling inefficiencies in IE's and Safari's regexp engines. First we
+ // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
+ // replace all simple value tokens with ']' characters. Third, we delete all
+ // open brackets that follow a colon or comma or that begin the text. Finally,
+ // we look to see that the remaining characters are only whitespace or ']' or
+ // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
+
+ if (/^[\],:{}\s]*$/
+ .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
+ .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
+ .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
+
+ // In the third stage we use the eval function to compile the text into a
+ // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
+ // in JavaScript: it can begin a block or an object literal. We wrap the text
+ // in parens to eliminate the ambiguity.
+
+ j = eval('(' + text + ')');
+
+ // In the optional fourth stage, we recursively walk the new structure, passing
+ // each name/value pair to a reviver function for possible transformation.
+
+ return typeof reviver === 'function' ?
+ walk({'': j}, '') : j;
+ }
+
+ // If the text is not JSON parseable, then a SyntaxError is thrown.
+
+ throw new SyntaxError('JSON.parse');
+ };
+
+ })(
+ 'undefined' != typeof io ? io : module.exports
+ , typeof JSON !== 'undefined' ? JSON : undefined
+ );
+
+ /**
+ * socket.io
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+ (function (exports, io) {
+
+ /**
+ * Parser namespace.
+ *
+ * @namespace
+ */
+
+ var parser = exports.parser = {};
+
+ /**
+ * Packet types.
+ */
+
+ var packets = parser.packets = [
+ 'disconnect'
+ , 'connect'
+ , 'heartbeat'
+ , 'message'
+ , 'json'
+ , 'event'
+ , 'ack'
+ , 'error'
+ , 'noop'
+ ];
+
+ /**
+ * Errors reasons.
+ */
+
+ var reasons = parser.reasons = [
+ 'transport not supported'
+ , 'client not handshaken'
+ , 'unauthorized'
+ ];
+
+ /**
+ * Errors advice.
+ */
+
+ var advice = parser.advice = [
+ 'reconnect'
+ ];
+
+ /**
+ * Shortcuts.
+ */
+
+ var JSON = io.JSON
+ , indexOf = io.util.indexOf;
+
+ /**
+ * Encodes a packet.
+ *
+ * @api private
+ */
+
+ parser.encodePacket = function (packet) {
+ var type = indexOf(packets, packet.type)
+ , id = packet.id || ''
+ , endpoint = packet.endpoint || ''
+ , ack = packet.ack
+ , data = null;
+
+ switch (packet.type) {
+ case 'error':
+ var reason = packet.reason ? indexOf(reasons, packet.reason) : ''
+ , adv = packet.advice ? indexOf(advice, packet.advice) : '';
+
+ if (reason !== '' || adv !== '')
+ data = reason + (adv !== '' ? ('+' + adv) : '');
+
+ break;
+
+ case 'message':
+ if (packet.data !== '')
+ data = packet.data;
+ break;
+
+ case 'event':
+ var ev = { name: packet.name };
+
+ if (packet.args && packet.args.length) {
+ ev.args = packet.args;
+ }
+
+ data = JSON.stringify(ev);
+ break;
+
+ case 'json':
+ data = JSON.stringify(packet.data);
+ break;
+
+ case 'connect':
+ if (packet.qs)
+ data = packet.qs;
+ break;
+
+ case 'ack':
+ data = packet.ackId
+ + (packet.args && packet.args.length
+ ? '+' + JSON.stringify(packet.args) : '');
+ break;
+ }
+
+ // construct packet with required fragments
+ var encoded = [
+ type
+ , id + (ack == 'data' ? '+' : '')
+ , endpoint
+ ];
+
+ // data fragment is optional
+ if (data !== null && data !== undefined)
+ encoded.push(data);
+
+ return encoded.join(':');
+ };
+
+ /**
+ * Encodes multiple messages (payload).
+ *
+ * @param {Array} messages
+ * @api private
+ */
+
+ parser.encodePayload = function (packets) {
+ var decoded = '';
+
+ if (packets.length == 1)
+ return packets[0];
+
+ for (var i = 0, l = packets.length; i < l; i++) {
+ var packet = packets[i];
+ decoded += '\ufffd' + packet.length + '\ufffd' + packets[i];
+ }
+
+ return decoded;
+ };
+
+ /**
+ * Decodes a packet
+ *
+ * @api private
+ */
+
+ var regexp = /([^:]+):([0-9]+)?(\+)?:([^:]+)?:?([\s\S]*)?/;
+
+ parser.decodePacket = function (data) {
+ var pieces = data.match(regexp);
+
+ if (!pieces) return {};
+
+ var id = pieces[2] || ''
+ , data = pieces[5] || ''
+ , packet = {
+ type: packets[pieces[1]]
+ , endpoint: pieces[4] || ''
+ };
+
+ // whether we need to acknowledge the packet
+ if (id) {
+ packet.id = id;
+ if (pieces[3])
+ packet.ack = 'data';
+ else
+ packet.ack = true;
+ }
+
+ // handle different packet types
+ switch (packet.type) {
+ case 'error':
+ var pieces = data.split('+');
+ packet.reason = reasons[pieces[0]] || '';
+ packet.advice = advice[pieces[1]] || '';
+ break;
+
+ case 'message':
+ packet.data = data || '';
+ break;
+
+ case 'event':
+ try {
+ var opts = JSON.parse(data);
+ packet.name = opts.name;
+ packet.args = opts.args;
+ } catch (e) { }
+
+ packet.args = packet.args || [];
+ break;
+
+ case 'json':
+ try {
+ packet.data = JSON.parse(data);
+ } catch (e) { }
+ break;
+
+ case 'connect':
+ packet.qs = data || '';
+ break;
+
+ case 'ack':
+ var pieces = data.match(/^([0-9]+)(\+)?(.*)/);
+ if (pieces) {
+ packet.ackId = pieces[1];
+ packet.args = [];
+
+ if (pieces[3]) {
+ try {
+ packet.args = pieces[3] ? JSON.parse(pieces[3]) : [];
+ } catch (e) { }
+ }
+ }
+ break;
+
+ case 'disconnect':
+ case 'heartbeat':
+ break;
+ };
+
+ return packet;
+ };
+
+ /**
+ * Decodes data payload. Detects multiple messages
+ *
+ * @return {Array} messages
+ * @api public
+ */
+
+ parser.decodePayload = function (data) {
+ // IE doesn't like data[i] for unicode chars, charAt works fine
+ if (data.charAt(0) == '\ufffd') {
+ var ret = [];
+
+ for (var i = 1, length = ''; i < data.length; i++) {
+ if (data.charAt(i) == '\ufffd') {
+ ret.push(parser.decodePacket(data.substr(i + 1).substr(0, length)));
+ i += Number(length) + 1;
+ length = '';
+ } else {
+ length += data.charAt(i);
+ }
+ }
+
+ return ret;
+ } else {
+ return [parser.decodePacket(data)];
+ }
+ };
+
+ })(
+ 'undefined' != typeof io ? io : module.exports
+ , 'undefined' != typeof io ? io : module.parent.exports
+ );
+ /**
+ * socket.io
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+ (function (exports, io) {
+
+ /**
+ * Expose constructor.
+ */
+
+ exports.Transport = Transport;
+
+ /**
+ * This is the transport template for all supported transport methods.
+ *
+ * @constructor
+ * @api public
+ */
+
+ function Transport (socket, sessid) {
+ this.socket = socket;
+ this.sessid = sessid;
+ };
+
+ /**
+ * Apply EventEmitter mixin.
+ */
+
+ io.util.mixin(Transport, io.EventEmitter);
+
+
+ /**
+ * Indicates whether heartbeats is enabled for this transport
+ *
+ * @api private
+ */
+
+ Transport.prototype.heartbeats = function () {
+ return true;
+ };
+
+ /**
+ * Handles the response from the server. When a new response is received
+ * it will automatically update the timeout, decode the message and
+ * forwards the response to the onMessage function for further processing.
+ *
+ * @param {String} data Response from the server.
+ * @api private
+ */
+
+ Transport.prototype.onData = function (data) {
+ this.clearCloseTimeout();
+
+ // If the connection in currently open (or in a reopening state) reset the close
+ // timeout since we have just received data. This check is necessary so
+ // that we don't reset the timeout on an explicitly disconnected connection.
+ if (this.socket.connected || this.socket.connecting || this.socket.reconnecting) {
+ this.setCloseTimeout();
+ }
+
+ if (data !== '') {
+ // todo: we should only do decodePayload for xhr transports
+ var msgs = io.parser.decodePayload(data);
+
+ if (msgs && msgs.length) {
+ for (var i = 0, l = msgs.length; i < l; i++) {
+ this.onPacket(msgs[i]);
+ }
+ }
+ }
+
+ return this;
+ };
+
+ /**
+ * Handles packets.
+ *
+ * @api private
+ */
+
+ Transport.prototype.onPacket = function (packet) {
+ this.socket.setHeartbeatTimeout();
+
+ if (packet.type == 'heartbeat') {
+ return this.onHeartbeat();
+ }
+
+ if (packet.type == 'connect' && packet.endpoint == '') {
+ this.onConnect();
+ }
+
+ if (packet.type == 'error' && packet.advice == 'reconnect') {
+ this.isOpen = false;
+ }
+
+ this.socket.onPacket(packet);
+
+ return this;
+ };
+
+ /**
+ * Sets close timeout
+ *
+ * @api private
+ */
+
+ Transport.prototype.setCloseTimeout = function () {
+ if (!this.closeTimeout) {
+ var self = this;
+
+ this.closeTimeout = setTimeout(function () {
+ self.onDisconnect();
+ }, this.socket.closeTimeout);
+ }
+ };
+
+ /**
+ * Called when transport disconnects.
+ *
+ * @api private
+ */
+
+ Transport.prototype.onDisconnect = function () {
+ if (this.isOpen) this.close();
+ this.clearTimeouts();
+ this.socket.onDisconnect();
+ return this;
+ };
+
+ /**
+ * Called when transport connects
+ *
+ * @api private
+ */
+
+ Transport.prototype.onConnect = function () {
+ this.socket.onConnect();
+ return this;
+ };
+
+ /**
+ * Clears close timeout
+ *
+ * @api private
+ */
+
+ Transport.prototype.clearCloseTimeout = function () {
+ if (this.closeTimeout) {
+ clearTimeout(this.closeTimeout);
+ this.closeTimeout = null;
+ }
+ };
+
+ /**
+ * Clear timeouts
+ *
+ * @api private
+ */
+
+ Transport.prototype.clearTimeouts = function () {
+ this.clearCloseTimeout();
+
+ if (this.reopenTimeout) {
+ clearTimeout(this.reopenTimeout);
+ }
+ };
+
+ /**
+ * Sends a packet
+ *
+ * @param {Object} packet object.
+ * @api private
+ */
+
+ Transport.prototype.packet = function (packet) {
+ this.send(io.parser.encodePacket(packet));
+ };
+
+ /**
+ * Send the received heartbeat message back to server. So the server
+ * knows we are still connected.
+ *
+ * @param {String} heartbeat Heartbeat response from the server.
+ * @api private
+ */
+
+ Transport.prototype.onHeartbeat = function (heartbeat) {
+ this.packet({ type: 'heartbeat' });
+ };
+
+ /**
+ * Called when the transport opens.
+ *
+ * @api private
+ */
+
+ Transport.prototype.onOpen = function () {
+ this.isOpen = true;
+ this.clearCloseTimeout();
+ this.socket.onOpen();
+ };
+
+ /**
+ * Notifies the base when the connection with the Socket.IO server
+ * has been disconnected.
+ *
+ * @api private
+ */
+
+ Transport.prototype.onClose = function () {
+ var self = this;
+
+ /* FIXME: reopen delay causing a infinit loop
+ this.reopenTimeout = setTimeout(function () {
+ self.open();
+ }, this.socket.options['reopen delay']);*/
+
+ this.isOpen = false;
+ this.socket.onClose();
+ this.onDisconnect();
+ };
+
+ /**
+ * Generates a connection url based on the Socket.IO URL Protocol.
+ * See <https://github.com/learnboost/socket.io-node/> for more details.
+ *
+ * @returns {String} Connection url
+ * @api private
+ */
+
+ Transport.prototype.prepareUrl = function () {
+ var options = this.socket.options;
+
+ return this.scheme() + '://'
+ + options.host + ':' + options.port + '/'
+ + options.resource + '/' + io.protocol
+ + '/' + this.name + '/' + this.sessid;
+ };
+
+ /**
+ * Checks if the transport is ready to start a connection.
+ *
+ * @param {Socket} socket The socket instance that needs a transport
+ * @param {Function} fn The callback
+ * @api private
+ */
+
+ Transport.prototype.ready = function (socket, fn) {
+ fn.call(this);
+ };
+ })(
+ 'undefined' != typeof io ? io : module.exports
+ , 'undefined' != typeof io ? io : module.parent.exports
+ );
+ /**
+ * socket.io
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+ (function (exports, io, global) {
+
+ /**
+ * Expose constructor.
+ */
+
+ exports.Socket = Socket;
+
+ /**
+ * Create a new `Socket.IO client` which can establish a persistent
+ * connection with a Socket.IO enabled server.
+ *
+ * @api public
+ */
+
+ function Socket (options) {
+ this.options = {
+ port: 80
+ , secure: false
+ , document: 'document' in global ? document : false
+ , resource: 'socket.io'
+ , transports: io.transports
+ , 'connect timeout': 10000
+ , 'try multiple transports': true
+ , 'reconnect': true
+ , 'reconnection delay': 500
+ , 'reconnection limit': Infinity
+ , 'reopen delay': 3000
+ , 'max reconnection attempts': 10
+ , 'sync disconnect on unload': false
+ , 'auto connect': true
+ , 'flash policy port': 10843
+ , 'manualFlush': false
+ };
+
+ io.util.merge(this.options, options);
+
+ this.connected = false;
+ this.open = false;
+ this.connecting = false;
+ this.reconnecting = false;
+ this.namespaces = {};
+ this.buffer = [];
+ this.doBuffer = false;
+
+ if (this.options['sync disconnect on unload'] &&
+ (!this.isXDomain() || io.util.ua.hasCORS)) {
+ var self = this;
+ io.util.on(global, 'beforeunload', function () {
+ self.disconnectSync();
+ }, false);
+ }
+
+ if (this.options['auto connect']) {
+ this.connect();
+ }
+ };
+
+ /**
+ * Apply EventEmitter mixin.
+ */
+
+ io.util.mixin(Socket, io.EventEmitter);
+
+ /**
+ * Returns a namespace listener/emitter for this socket
+ *
+ * @api public
+ */
+
+ Socket.prototype.of = function (name) {
+ if (!this.namespaces[name]) {
+ this.namespaces[name] = new io.SocketNamespace(this, name);
+
+ if (name !== '') {
+ this.namespaces[name].packet({ type: 'connect' });
+ }
+ }
+
+ return this.namespaces[name];
+ };
+
+ /**
+ * Emits the given event to the Socket and all namespaces
+ *
+ * @api private
+ */
+
+ Socket.prototype.publish = function () {
+ this.emit.apply(this, arguments);
+
+ var nsp;
+
+ for (var i in this.namespaces) {
+ if (this.namespaces.hasOwnProperty(i)) {
+ nsp = this.of(i);
+ nsp.$emit.apply(nsp, arguments);
+ }
+ }
+ };
+
+ /**
+ * Performs the handshake
+ *
+ * @api private
+ */
+
+ function empty () { };
+
+ Socket.prototype.handshake = function (fn) {
+ var self = this
+ , options = this.options;
+
+ function complete (data) {
+ if (data instanceof Error) {
+ self.connecting = false;
+ self.onError(data.message);
+ } else {
+ fn.apply(null, data.split(':'));
+ }
+ };
+
+ var url = [
+ 'http' + (options.secure ? 's' : '') + ':/'
+ , options.host + ':' + options.port
+ , options.resource
+ , io.protocol
+ , io.util.query(this.options.query, 't=' + +new Date)
+ ].join('/');
+
+ if (this.isXDomain() && !io.util.ua.hasCORS) {
+ var insertAt = document.getElementsByTagName('script')[0]
+ , script = document.createElement('script');
+
+ script.src = url + '&jsonp=' + io.j.length;
+ insertAt.parentNode.insertBefore(script, insertAt);
+
+ io.j.push(function (data) {
+ complete(data);
+ script.parentNode.removeChild(script);
+ });
+ } else {
+ var xhr = io.util.request();
+
+ xhr.open('GET', url, true);
+ if (this.isXDomain()) {
+ xhr.withCredentials = true;
+ }
+ xhr.onreadystatechange = function () {
+ if (xhr.readyState == 4) {
+ xhr.onreadystatechange = empty;
+
+ if (xhr.status == 200) {
+ complete(xhr.responseText);
+ } else if (xhr.status == 403) {
+ self.onError(xhr.responseText);
+ } else {
+ self.connecting = false;
+ !self.reconnecting && self.onError(xhr.responseText);
+ }
+ }
+ };
+ xhr.send(null);
+ }
+ };
+
+ /**
+ * Find an available transport based on the options supplied in the constructor.
+ *
+ * @api private
+ */
+
+ Socket.prototype.getTransport = function (override) {
+ var transports = override || this.transports, match;
+
+ for (var i = 0, transport; transport = transports[i]; i++) {
+ if (io.Transport[transport]
+ && io.Transport[transport].check(this)
+ && (!this.isXDomain() || io.Transport[transport].xdomainCheck(this))) {
+ return new io.Transport[transport](this, this.sessionid);
+ }
+ }
+
+ return null;
+ };
+
+ /**
+ * Connects to the server.
+ *
+ * @param {Function} [fn] Callback.
+ * @returns {io.Socket}
+ * @api public
+ */
+
+ Socket.prototype.connect = function (fn) {
+ if (this.connecting) {
+ return this;
+ }
+
+ var self = this;
+ self.connecting = true;
+
+ this.handshake(function (sid, heartbeat, close, transports) {
+ self.sessionid = sid;
+ self.closeTimeout = close * 1000;
+ self.heartbeatTimeout = heartbeat * 1000;
+ if(!self.transports)
+ self.transports = self.origTransports = (transports ? io.util.intersect(
+ transports.split(',')
+ , self.options.transports
+ ) : self.options.transports);
+
+ self.setHeartbeatTimeout();
+
+ function connect (transports){
+ if (self.transport) self.transport.clearTimeouts();
+
+ self.transport = self.getTransport(transports);
+ if (!self.transport) return self.publish('connect_failed');
+
+ // once the transport is ready
+ self.transport.ready(self, function () {
+ self.connecting = true;
+ self.publish('connecting', self.transport.name);
+ self.transport.open();
+
+ if (self.options['connect timeout']) {
+ self.connectTimeoutTimer = setTimeout(function () {
+ if (!self.connected) {
+ self.connecting = false;
+
+ if (self.options['try multiple transports']) {
+ var remaining = self.transports;
+
+ while (remaining.length > 0 && remaining.splice(0,1)[0] !=
+ self.transport.name) {}
+
+ if (remaining.length){
+ connect(remaining);
+ } else {
+ self.publish('connect_failed');
+ }
+ }
+ }
+ }, self.options['connect timeout']);
+ }
+ });
+ }
+
+ connect(self.transports);
+
+ self.once('connect', function (){
+ clearTimeout(self.connectTimeoutTimer);
+
+ fn && typeof fn == 'function' && fn();
+ });
+ });
+
+ return this;
+ };
+
+ /**
+ * Clears and sets a new heartbeat timeout using the value given by the
+ * server during the handshake.
+ *
+ * @api private
+ */
+
+ Socket.prototype.setHeartbeatTimeout = function () {
+ clearTimeout(this.heartbeatTimeoutTimer);
+ if(this.transport && !this.transport.heartbeats()) return;
+
+ var self = this;
+ this.heartbeatTimeoutTimer = setTimeout(function () {
+ self.transport.onClose();
+ }, this.heartbeatTimeout);
+ };
+
+ /**
+ * Sends a message.
+ *
+ * @param {Object} data packet.
+ * @returns {io.Socket}
+ * @api public
+ */
+
+ Socket.prototype.packet = function (data) {
+ if (this.connected && !this.doBuffer) {
+ this.transport.packet(data);
+ } else {
+ this.buffer.push(data);
+ }
+
+ return this;
+ };
+
+ /**
+ * Sets buffer state
+ *
+ * @api private
+ */
+
+ Socket.prototype.setBuffer = function (v) {
+ this.doBuffer = v;
+
+ if (!v && this.connected && this.buffer.length) {
+ if (!this.options['manualFlush']) {
+ this.flushBuffer();
+ }
+ }
+ };
+
+ /**
+ * Flushes the buffer data over the wire.
+ * To be invoked manually when 'manualFlush' is set to true.
+ *
+ * @api public
+ */
+
+ Socket.prototype.flushBuffer = function() {
+ this.transport.payload(this.buffer);
+ this.buffer = [];
+ };
+
+
+ /**
+ * Disconnect the established connect.
+ *
+ * @returns {io.Socket}
+ * @api public
+ */
+
+ Socket.prototype.disconnect = function () {
+ if (this.connected || this.connecting) {
+ if (this.open) {
+ this.of('').packet({ type: 'disconnect' });
+ }
+
+ // handle disconnection immediately
+ this.onDisconnect('booted');
+ }
+
+ return this;
+ };
+
+ /**
+ * Disconnects the socket with a sync XHR.
+ *
+ * @api private
+ */
+
+ Socket.prototype.disconnectSync = function () {
+ // ensure disconnection
+ var xhr = io.util.request();
+ var uri = [
+ 'http' + (this.options.secure ? 's' : '') + ':/'
+ , this.options.host + ':' + this.options.port
+ , this.options.resource
+ , io.protocol
+ , ''
+ , this.sessionid
+ ].join('/') + '/?disconnect=1';
+
+ xhr.open('GET', uri, false);
+ xhr.send(null);
+
+ // handle disconnection immediately
+ this.onDisconnect('booted');
+ };
+
+ /**
+ * Check if we need to use cross domain enabled transports. Cross domain would
+ * be a different port or different domain name.
+ *
+ * @returns {Boolean}
+ * @api private
+ */
+
+ Socket.prototype.isXDomain = function () {
+
+ var port = global.location.port ||
+ ('https:' == global.location.protocol ? 443 : 80);
+
+ return this.options.host !== global.location.hostname
+ || this.options.port != port;
+ };
+
+ /**
+ * Called upon handshake.
+ *
+ * @api private
+ */
+
+ Socket.prototype.onConnect = function () {
+ if (!this.connected) {
+ this.connected = true;
+ this.connecting = false;
+ if (!this.doBuffer) {
+ // make sure to flush the buffer
+ this.setBuffer(false);
+ }
+ this.emit('connect');
+ }
+ };
+
+ /**
+ * Called when the transport opens
+ *
+ * @api private
+ */
+
+ Socket.prototype.onOpen = function () {
+ this.open = true;
+ };
+
+ /**
+ * Called when the transport closes.
+ *
+ * @api private
+ */
+
+ Socket.prototype.onClose = function () {
+ this.open = false;
+ clearTimeout(this.heartbeatTimeoutTimer);
+ };
+
+ /**
+ * Called when the transport first opens a connection
+ *
+ * @param text
+ */
+
+ Socket.prototype.onPacket = function (packet) {
+ this.of(packet.endpoint).onPacket(packet);
+ };
+
+ /**
+ * Handles an error.
+ *
+ * @api private
+ */
+
+ Socket.prototype.onError = function (err) {
+ if (err && err.advice) {
+ if (err.advice === 'reconnect' && (this.connected || this.connecting)) {
+ this.disconnect();
+ if (this.options.reconnect) {
+ this.reconnect();
+ }
+ }
+ }
+
+ this.publish('error', err && err.reason ? err.reason : err);
+ };
+
+ /**
+ * Called when the transport disconnects.
+ *
+ * @api private
+ */
+
+ Socket.prototype.onDisconnect = function (reason) {
+ var wasConnected = this.connected
+ , wasConnecting = this.connecting;
+
+ this.connected = false;
+ this.connecting = false;
+ this.open = false;
+
+ if (wasConnected || wasConnecting) {
+ this.transport.close();
+ this.transport.clearTimeouts();
+ if (wasConnected) {
+ this.publish('disconnect', reason);
+
+ if ('booted' != reason && this.options.reconnect && !this.reconnecting) {
+ this.reconnect();
+ }
+ }
+ }
+ };
+
+ /**
+ * Called upon reconnection.
+ *
+ * @api private
+ */
+
+ Socket.prototype.reconnect = function () {
+ this.reconnecting = true;
+ this.reconnectionAttempts = 0;
+ this.reconnectionDelay = this.options['reconnection delay'];
+
+ var self = this
+ , maxAttempts = this.options['max reconnection attempts']
+ , tryMultiple = this.options['try multiple transports']
+ , limit = this.options['reconnection limit'];
+
+ function reset () {
+ if (self.connected) {
+ for (var i in self.namespaces) {
+ if (self.namespaces.hasOwnProperty(i) && '' !== i) {
+ self.namespaces[i].packet({ type: 'connect' });
+ }
+ }
+ self.publish('reconnect', self.transport.name, self.reconnectionAttempts);
+ }
+
+ clearTimeout(self.reconnectionTimer);
+
+ self.removeListener('connect_failed', maybeReconnect);
+ self.removeListener('connect', maybeReconnect);
+
+ self.reconnecting = false;
+
+ delete self.reconnectionAttempts;
+ delete self.reconnectionDelay;
+ delete self.reconnectionTimer;
+ delete self.redoTransports;
+
+ self.options['try multiple transports'] = tryMultiple;
+ };
+
+ function maybeReconnect () {
+ if (!self.reconnecting) {
+ return;
+ }
+
+ if (self.connected) {
+ return reset();
+ };
+
+ if (self.connecting && self.reconnecting) {
+ return self.reconnectionTimer = setTimeout(maybeReconnect, 1000);
+ }
+
+ if (self.reconnectionAttempts++ >= maxAttempts) {
+ if (!self.redoTransports) {
+ self.on('connect_failed', maybeReconnect);
+ self.options['try multiple transports'] = true;
+ self.transports = self.origTransports;
+ self.transport = self.getTransport();
+ self.redoTransports = true;
+ self.connect();
+ } else {
+ self.publish('reconnect_failed');
+ reset();
+ }
+ } else {
+ if (self.reconnectionDelay < limit) {
+ self.reconnectionDelay *= 2; // exponential back off
+ }
+
+ self.connect();
+ self.publish('reconnecting', self.reconnectionDelay, self.reconnectionAttempts);
+ self.reconnectionTimer = setTimeout(maybeReconnect, self.reconnectionDelay);
+ }
+ };
+
+ this.options['try multiple transports'] = false;
+ this.reconnectionTimer = setTimeout(maybeReconnect, this.reconnectionDelay);
+
+ this.on('connect', maybeReconnect);
+ };
+
+ })(
+ 'undefined' != typeof io ? io : module.exports
+ , 'undefined' != typeof io ? io : module.parent.exports
+ , global
+ );
+ /**
+ * socket.io
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+ (function (exports, io) {
+
+ /**
+ * Expose constructor.
+ */
+
+ exports.SocketNamespace = SocketNamespace;
+
+ /**
+ * Socket namespace constructor.
+ *
+ * @constructor
+ * @api public
+ */
+
+ function SocketNamespace (socket, name) {
+ this.socket = socket;
+ this.name = name || '';
+ this.flags = {};
+ this.json = new Flag(this, 'json');
+ this.ackPackets = 0;
+ this.acks = {};
+ };
+
+ /**
+ * Apply EventEmitter mixin.
+ */
+
+ io.util.mixin(SocketNamespace, io.EventEmitter);
+
+ /**
+ * Copies emit since we override it
+ *
+ * @api private
+ */
+
+ SocketNamespace.prototype.$emit = io.EventEmitter.prototype.emit;
+
+ /**
+ * Creates a new namespace, by proxying the request to the socket. This
+ * allows us to use the synax as we do on the server.
+ *
+ * @api public
+ */
+
+ SocketNamespace.prototype.of = function () {
+ return this.socket.of.apply(this.socket, arguments);
+ };
+
+ /**
+ * Sends a packet.
+ *
+ * @api private
+ */
+
+ SocketNamespace.prototype.packet = function (packet) {
+ packet.endpoint = this.name;
+ this.socket.packet(packet);
+ this.flags = {};
+ return this;
+ };
+
+ /**
+ * Sends a message
+ *
+ * @api public
+ */
+
+ SocketNamespace.prototype.send = function (data, fn) {
+ var packet = {
+ type: this.flags.json ? 'json' : 'message'
+ , data: data
+ };
+
+ if ('function' == typeof fn) {
+ packet.id = ++this.ackPackets;
+ packet.ack = true;
+ this.acks[packet.id] = fn;
+ }
+
+ return this.packet(packet);
+ };
+
+ /**
+ * Emits an event
+ *
+ * @api public
+ */
+
+ SocketNamespace.prototype.emit = function (name) {
+ var args = Array.prototype.slice.call(arguments, 1)
+ , lastArg = args[args.length - 1]
+ , packet = {
+ type: 'event'
+ , name: name
+ };
+
+ if ('function' == typeof lastArg) {
+ packet.id = ++this.ackPackets;
+ packet.ack = 'data';
+ this.acks[packet.id] = lastArg;
+ args = args.slice(0, args.length - 1);
+ }
+
+ packet.args = args;
+
+ return this.packet(packet);
+ };
+
+ /**
+ * Disconnects the namespace
+ *
+ * @api private
+ */
+
+ SocketNamespace.prototype.disconnect = function () {
+ if (this.name === '') {
+ this.socket.disconnect();
+ } else {
+ this.packet({ type: 'disconnect' });
+ this.$emit('disconnect');
+ }
+
+ return this;
+ };
+
+ /**
+ * Handles a packet
+ *
+ * @api private
+ */
+
+ SocketNamespace.prototype.onPacket = function (packet) {
+ var self = this;
+
+ function ack () {
+ self.packet({
+ type: 'ack'
+ , args: io.util.toArray(arguments)
+ , ackId: packet.id
+ });
+ };
+
+ switch (packet.type) {
+ case 'connect':
+ this.$emit('connect');
+ break;
+
+ case 'disconnect':
+ if (this.name === '') {
+ this.socket.onDisconnect(packet.reason || 'booted');
+ } else {
+ this.$emit('disconnect', packet.reason);
+ }
+ break;
+
+ case 'message':
+ case 'json':
+ var params = ['message', packet.data];
+
+ if (packet.ack == 'data') {
+ params.push(ack);
+ } else if (packet.ack) {
+ this.packet({ type: 'ack', ackId: packet.id });
+ }
+
+ this.$emit.apply(this, params);
+ break;
+
+ case 'event':
+ var params = [packet.name].concat(packet.args);
+
+ if (packet.ack == 'data')
+ params.push(ack);
+
+ this.$emit.apply(this, params);
+ break;
+
+ case 'ack':
+ if (this.acks[packet.ackId]) {
+ this.acks[packet.ackId].apply(this, packet.args);
+ delete this.acks[packet.ackId];
+ }
+ break;
+
+ case 'error':
+ if (packet.advice){
+ this.socket.onError(packet);
+ } else {
+ if (packet.reason == 'unauthorized') {
+ this.$emit('connect_failed', packet.reason);
+ } else {
+ this.$emit('error', packet.reason);
+ }
+ }
+ break;
+ }
+ };
+
+ /**
+ * Flag interface.
+ *
+ * @api private
+ */
+
+ function Flag (nsp, name) {
+ this.namespace = nsp;
+ this.name = name;
+ };
+
+ /**
+ * Send a message
+ *
+ * @api public
+ */
+
+ Flag.prototype.send = function () {
+ this.namespace.flags[this.name] = true;
+ this.namespace.send.apply(this.namespace, arguments);
+ };
+
+ /**
+ * Emit an event
+ *
+ * @api public
+ */
+
+ Flag.prototype.emit = function () {
+ this.namespace.flags[this.name] = true;
+ this.namespace.emit.apply(this.namespace, arguments);
+ };
+
+ })(
+ 'undefined' != typeof io ? io : module.exports
+ , 'undefined' != typeof io ? io : module.parent.exports
+ );
+
+ /**
+ * socket.io
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+ (function (exports, io, global) {
+
+ /**
+ * Expose constructor.
+ */
+
+ exports.websocket = WS;
+
+ /**
+ * The WebSocket transport uses the HTML5 WebSocket API to establish an
+ * persistent connection with the Socket.IO server. This transport will also
+ * be inherited by the FlashSocket fallback as it provides a API compatible
+ * polyfill for the WebSockets.
+ *
+ * @constructor
+ * @extends {io.Transport}
+ * @api public
+ */
+
+ function WS (socket) {
+ io.Transport.apply(this, arguments);
+ };
+
+ /**
+ * Inherits from Transport.
+ */
+
+ io.util.inherit(WS, io.Transport);
+
+ /**
+ * Transport name
+ *
+ * @api public
+ */
+
+ WS.prototype.name = 'websocket';
+
+ /**
+ * Initializes a new `WebSocket` connection with the Socket.IO server. We attach
+ * all the appropriate listeners to handle the responses from the server.
+ *
+ * @returns {Transport}
+ * @api public
+ */
+
+ WS.prototype.open = function () {
+ var query = io.util.query(this.socket.options.query)
+ , self = this
+ , Socket
+
+
+ if (!Socket) {
+ Socket = global.MozWebSocket || global.WebSocket;
+ }
+
+ this.websocket = new Socket(this.prepareUrl() + query);
+
+ this.websocket.onopen = function () {
+ self.onOpen();
+ self.socket.setBuffer(false);
+ };
+ this.websocket.onmessage = function (ev) {
+ self.onData(ev.data);
+ };
+ this.websocket.onclose = function () {
+ self.onClose();
+ self.socket.setBuffer(true);
+ };
+ this.websocket.onerror = function (e) {
+ self.onError(e);
+ };
+
+ return this;
+ };
+
+ /**
+ * Send a message to the Socket.IO server. The message will automatically be
+ * encoded in the correct message format.
+ *
+ * @returns {Transport}
+ * @api public
+ */
+
+ // Do to a bug in the current IDevices browser, we need to wrap the send in a
+ // setTimeout, when they resume from sleeping the browser will crash if
+ // we don't allow the browser time to detect the socket has been closed
+ if (io.util.ua.iDevice) {
+ WS.prototype.send = function (data) {
+ var self = this;
+ setTimeout(function() {
+ self.websocket.send(data);
+ },0);
+ return this;
+ };
+ } else {
+ WS.prototype.send = function (data) {
+ this.websocket.send(data);
+ return this;
+ };
+ }
+
+ /**
+ * Payload
+ *
+ * @api private
+ */
+
+ WS.prototype.payload = function (arr) {
+ for (var i = 0, l = arr.length; i < l; i++) {
+ this.packet(arr[i]);
+ }
+ return this;
+ };
+
+ /**
+ * Disconnect the established `WebSocket` connection.
+ *
+ * @returns {Transport}
+ * @api public
+ */
+
+ WS.prototype.close = function () {
+ this.websocket.close();
+ return this;
+ };
+
+ /**
+ * Handle the errors that `WebSocket` might be giving when we
+ * are attempting to connect or send messages.
+ *
+ * @param {Error} e The error.
+ * @api private
+ */
+
+ WS.prototype.onError = function (e) {
+ this.socket.onError(e);
+ };
+
+ /**
+ * Returns the appropriate scheme for the URI generation.
+ *
+ * @api private
+ */
+ WS.prototype.scheme = function () {
+ return this.socket.options.secure ? 'wss' : 'ws';
+ };
+
+ /**
+ * Checks if the browser has support for native `WebSockets` and that
+ * it's not the polyfill created for the FlashSocket transport.
+ *
+ * @return {Boolean}
+ * @api public
+ */
+
+ WS.check = function () {
+ return ('WebSocket' in global && !('__addTask' in WebSocket))
+ || 'MozWebSocket' in global;
+ };
+
+ /**
+ * Check if the `WebSocket` transport support cross domain communications.
+ *
+ * @returns {Boolean}
+ * @api public
+ */
+
+ WS.xdomainCheck = function () {
+ return true;
+ };
+
+ /**
+ * Add the transport to your public io.transports array.
+ *
+ * @api private
+ */
+
+ io.transports.push('websocket');
+
+ })(
+ 'undefined' != typeof io ? io.Transport : module.exports
+ , 'undefined' != typeof io ? io : module.parent.exports
+ , global
+ );
+
+ // Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
+ // License: New BSD License
+ // Reference: http://dev.w3.org/html5/websockets/
+ // Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol
+
+ (function() {
+
+ if ('undefined' == typeof window || window.WebSocket) return;
+
+ var console = window.console;
+ if (!console || !console.log || !console.error) {
+ console = {log: function(){ }, error: function(){ }};
+ }
+
+ if (!swfobject.hasFlashPlayerVersion("10.0.0")) {
+ console.error("Flash Player >= 10.0.0 is required.");
+ return;
+ }
+ if (location.protocol == "file:") {
+ console.error(
+ "WARNING: web-socket-js doesn't work in file:///... URL " +
+ "unless you set Flash Security Settings properly. " +
+ "Open the page via Web server i.e. http://...");
+ }
+
+ /**
+ * This class represents a faux web socket.
+ * @param {string} url
+ * @param {array or string} protocols
+ * @param {string} proxyHost
+ * @param {int} proxyPort
+ * @param {string} headers
+ */
+ WebSocket = function(url, protocols, proxyHost, proxyPort, headers) {
+ var self = this;
+ self.__id = WebSocket.__nextId++;
+ WebSocket.__instances[self.__id] = self;
+ self.readyState = WebSocket.CONNECTING;
+ self.bufferedAmount = 0;
+ self.__events = {};
+ if (!protocols) {
+ protocols = [];
+ } else if (typeof protocols == "string") {
+ protocols = [protocols];
+ }
+ // Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc.
+ // Otherwise, when onopen fires immediately, onopen is called before it is set.
+ setTimeout(function() {
+ WebSocket.__addTask(function() {
+ WebSocket.__flash.create(
+ self.__id, url, protocols, proxyHost || null, proxyPort || 0, headers || null);
+ });
+ }, 0);
+ };
+
+ /**
+ * Send data to the web socket.
+ * @param {string} data The data to send to the socket.
+ * @return {boolean} True for success, false for failure.
+ */
+ WebSocket.prototype.send = function(data) {
+ if (this.readyState == WebSocket.CONNECTING) {
+ throw "INVALID_STATE_ERR: Web Socket connection has not been established";
+ }
+ // We use encodeURIComponent() here, because FABridge doesn't work if
+ // the argument includes some characters. We don't use escape() here
+ // because of this:
+ // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Functions#escape_and_unescape_Functions
+ // But it looks decodeURIComponent(encodeURIComponent(s)) doesn't
+ // preserve all Unicode characters either e.g. "\uffff" in Firefox.
+ // Note by wtritch: Hopefully this will not be necessary using ExternalInterface. Will require
+ // additional testing.
+ var result = WebSocket.__flash.send(this.__id, encodeURIComponent(data));
+ if (result < 0) { // success
+ return true;
+ } else {
+ this.bufferedAmount += result;
+ return false;
+ }
+ };
+
+ /**
+ * Close this web socket gracefully.
+ */
+ WebSocket.prototype.close = function() {
+ if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) {
+ return;
+ }
+ this.readyState = WebSocket.CLOSING;
+ WebSocket.__flash.close(this.__id);
+ };
+
+ /**
+ * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
+ *
+ * @param {string} type
+ * @param {function} listener
+ * @param {boolean} useCapture
+ * @return void
+ */
+ WebSocket.prototype.addEventListener = function(type, listener, useCapture) {
+ if (!(type in this.__events)) {
+ this.__events[type] = [];
+ }
+ this.__events[type].push(listener);
+ };
+
+ /**
+ * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
+ *
+ * @param {string} type
+ * @param {function} listener
+ * @param {boolean} useCapture
+ * @return void
+ */
+ WebSocket.prototype.removeEventListener = function(type, listener, useCapture) {
+ if (!(type in this.__events)) return;
+ var events = this.__events[type];
+ for (var i = events.length - 1; i >= 0; --i) {
+ if (events[i] === listener) {
+ events.splice(i, 1);
+ break;
+ }
+ }
+ };
+
+ /**
+ * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
+ *
+ * @param {Event} event
+ * @return void
+ */
+ WebSocket.prototype.dispatchEvent = function(event) {
+ var events = this.__events[event.type] || [];
+ for (var i = 0; i < events.length; ++i) {
+ events[i](event);
+ }
+ var handler = this["on" + event.type];
+ if (handler) handler(event);
+ };
+
+ /**
+ * Handles an event from Flash.
+ * @param {Object} flashEvent
+ */
+ WebSocket.prototype.__handleEvent = function(flashEvent) {
+ if ("readyState" in flashEvent) {
+ this.readyState = flashEvent.readyState;
+ }
+ if ("protocol" in flashEvent) {
+ this.protocol = flashEvent.protocol;
+ }
+
+ var jsEvent;
+ if (flashEvent.type == "open" || flashEvent.type == "error") {
+ jsEvent = this.__createSimpleEvent(flashEvent.type);
+ } else if (flashEvent.type == "close") {
+ // TODO implement jsEvent.wasClean
+ jsEvent = this.__createSimpleEvent("close");
+ } else if (flashEvent.type == "message") {
+ var data = decodeURIComponent(flashEvent.message);
+ jsEvent = this.__createMessageEvent("message", data);
+ } else {
+ throw "unknown event type: " + flashEvent.type;
+ }
+
+ this.dispatchEvent(jsEvent);
+ };
+
+ WebSocket.prototype.__createSimpleEvent = function(type) {
+ if (document.createEvent && window.Event) {
+ var event = document.createEvent("Event");
+ event.initEvent(type, false, false);
+ return event;
+ } else {
+ return {type: type, bubbles: false, cancelable: false};
+ }
+ };
+
+ WebSocket.prototype.__createMessageEvent = function(type, data) {
+ if (document.createEvent && window.MessageEvent && !window.opera) {
+ var event = document.createEvent("MessageEvent");
+ event.initMessageEvent("message", false, false, data, null, null, window, null);
+ return event;
+ } else {
+ // IE and Opera, the latter one truncates the data parameter after any 0x00 bytes.
+ return {type: type, data: data, bubbles: false, cancelable: false};
+ }
+ };
+
+ /**
+ * Define the WebSocket readyState enumeration.
+ */
+ WebSocket.CONNECTING = 0;
+ WebSocket.OPEN = 1;
+ WebSocket.CLOSING = 2;
+ WebSocket.CLOSED = 3;
+
+ WebSocket.__flash = null;
+ WebSocket.__instances = {};
+ WebSocket.__tasks = [];
+ WebSocket.__nextId = 0;
+
+ /**
+ * Load a new flash security policy file.
+ * @param {string} url
+ */
+ WebSocket.loadFlashPolicyFile = function(url){
+ WebSocket.__addTask(function() {
+ WebSocket.__flash.loadManualPolicyFile(url);
+ });
+ };
+
+ /**
+ * Loads WebSocketMain.swf and creates WebSocketMain object in Flash.
+ */
+ WebSocket.__initialize = function() {
+ if (WebSocket.__flash) return;
+
+ if (WebSocket.__swfLocation) {
+ // For backword compatibility.
+ window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation;
+ }
+ if (!window.WEB_SOCKET_SWF_LOCATION) {
+ console.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf");
+ return;
+ }
+ var container = document.createElement("div");
+ container.id = "webSocketContainer";
+ // Hides Flash box. We cannot use display: none or visibility: hidden because it prevents
+ // Flash from loading at least in IE. So we move it out of the screen at (-100, -100).
+ // But this even doesn't work with Flash Lite (e.g. in Droid Incredible). So with Flash
+ // Lite, we put it at (0, 0). This shows 1x1 box visible at left-top corner but this is
+ // the best we can do as far as we know now.
+ container.style.position = "absolute";
+ if (WebSocket.__isFlashLite()) {
+ container.style.left = "0px";
+ container.style.top = "0px";
+ } else {
+ container.style.left = "-100px";
+ container.style.top = "-100px";
+ }
+ var holder = document.createElement("div");
+ holder.id = "webSocketFlash";
+ container.appendChild(holder);
+ document.body.appendChild(container);
+ // See this article for hasPriority:
+ // http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html
+ swfobject.embedSWF(
+ WEB_SOCKET_SWF_LOCATION,
+ "webSocketFlash",
+ "1" /* width */,
+ "1" /* height */,
+ "10.0.0" /* SWF version */,
+ null,
+ null,
+ {hasPriority: true, swliveconnect : true, allowScriptAccess: "always"},
+ null,
+ function(e) {
+ if (!e.success) {
+ console.error("[WebSocket] swfobject.embedSWF failed");
+ }
+ });
+ };
+
+ /**
+ * Called by Flash to notify JS that it's fully loaded and ready
+ * for communication.
+ */
+ WebSocket.__onFlashInitialized = function() {
+ // We need to set a timeout here to avoid round-trip calls
+ // to flash during the initialization process.
+ setTimeout(function() {
+ WebSocket.__flash = document.getElementById("webSocketFlash");
+ WebSocket.__flash.setCallerUrl(location.href);
+ WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG);
+ for (var i = 0; i < WebSocket.__tasks.length; ++i) {
+ WebSocket.__tasks[i]();
+ }
+ WebSocket.__tasks = [];
+ }, 0);
+ };
+
+ /**
+ * Called by Flash to notify WebSockets events are fired.
+ */
+ WebSocket.__onFlashEvent = function() {
+ setTimeout(function() {
+ try {
+ // Gets events using receiveEvents() instead of getting it from event object
+ // of Flash event. This is to make sure to keep message order.
+ // It seems sometimes Flash events don't arrive in the same order as they are sent.
+ var events = WebSocket.__flash.receiveEvents();
+ for (var i = 0; i < events.length; ++i) {
+ WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]);
+ }
+ } catch (e) {
+ console.error(e);
+ }
+ }, 0);
+ return true;
+ };
+
+ // Called by Flash.
+ WebSocket.__log = function(message) {
+ console.log(decodeURIComponent(message));
+ };
+
+ // Called by Flash.
+ WebSocket.__error = function(message) {
+ console.error(decodeURIComponent(message));
+ };
+
+ WebSocket.__addTask = function(task) {
+ if (WebSocket.__flash) {
+ task();
+ } else {
+ WebSocket.__tasks.push(task);
+ }
+ };
+
+ /**
+ * Test if the browser is running flash lite.
+ * @return {boolean} True if flash lite is running, false otherwise.
+ */
+ WebSocket.__isFlashLite = function() {
+ if (!window.navigator || !window.navigator.mimeTypes) {
+ return false;
+ }
+ var mimeType = window.navigator.mimeTypes["application/x-shockwave-flash"];
+ if (!mimeType || !mimeType.enabledPlugin || !mimeType.enabledPlugin.filename) {
+ return false;
+ }
+ return mimeType.enabledPlugin.filename.match(/flashlite/i) ? true : false;
+ };
+
+ if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) {
+ if (window.addEventListener) {
+ window.addEventListener("load", function(){
+ WebSocket.__initialize();
+ }, false);
+ } else {
+ window.attachEvent("onload", function(){
+ WebSocket.__initialize();
+ });
+ }
+ }
+
+ })();
+
+ /**
+ * socket.io
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+ (function (exports, io, global) {
+
+ /**
+ * Expose constructor.
+ *
+ * @api public
+ */
+
+ exports.XHR = XHR;
+
+ /**
+ * XHR constructor
+ *
+ * @costructor
+ * @api public
+ */
+
+ function XHR (socket) {
+ if (!socket) return;
+
+ io.Transport.apply(this, arguments);
+ this.sendBuffer = [];
+ };
+
+ /**
+ * Inherits from Transport.
+ */
+
+ io.util.inherit(XHR, io.Transport);
+
+ /**
+ * Establish a connection
+ *
+ * @returns {Transport}
+ * @api public
+ */
+
+ XHR.prototype.open = function () {
+ this.socket.setBuffer(false);
+ this.onOpen();
+ this.get();
+
+ // we need to make sure the request succeeds since we have no indication
+ // whether the request opened or not until it succeeded.
+ this.setCloseTimeout();
+
+ return this;
+ };
+
+ /**
+ * Check if we need to send data to the Socket.IO server, if we have data in our
+ * buffer we encode it and forward it to the `post` method.
+ *
+ * @api private
+ */
+
+ XHR.prototype.payload = function (payload) {
+ var msgs = [];
+
+ for (var i = 0, l = payload.length; i < l; i++) {
+ msgs.push(io.parser.encodePacket(payload[i]));
+ }
+
+ this.send(io.parser.encodePayload(msgs));
+ };
+
+ /**
+ * Send data to the Socket.IO server.
+ *
+ * @param data The message
+ * @returns {Transport}
+ * @api public
+ */
+
+ XHR.prototype.send = function (data) {
+ this.post(data);
+ return this;
+ };
+
+ /**
+ * Posts a encoded message to the Socket.IO server.
+ *
+ * @param {String} data A encoded message.
+ * @api private
+ */
+
+ function empty () { };
+
+ XHR.prototype.post = function (data) {
+ var self = this;
+ this.socket.setBuffer(true);
+
+ function stateChange () {
+ if (this.readyState == 4) {
+ this.onreadystatechange = empty;
+ self.posting = false;
+
+ if (this.status == 200){
+ self.socket.setBuffer(false);
+ } else {
+ self.onClose();
+ }
+ }
+ }
+
+ function onload () {
+ this.onload = empty;
+ self.socket.setBuffer(false);
+ };
+
+ this.sendXHR = this.request('POST');
+
+ if (global.XDomainRequest && this.sendXHR instanceof XDomainRequest) {
+ this.sendXHR.onload = this.sendXHR.onerror = onload;
+ } else {
+ this.sendXHR.onreadystatechange = stateChange;
+ }
+
+ this.sendXHR.send(data);
+ };
+
+ /**
+ * Disconnects the established `XHR` connection.
+ *
+ * @returns {Transport}
+ * @api public
+ */
+
+ XHR.prototype.close = function () {
+ this.onClose();
+ return this;
+ };
+
+ /**
+ * Generates a configured XHR request
+ *
+ * @param {String} url The url that needs to be requested.
+ * @param {String} method The method the request should use.
+ * @returns {XMLHttpRequest}
+ * @api private
+ */
+
+ XHR.prototype.request = function (method) {
+ var req = io.util.request(this.socket.isXDomain())
+ , query = io.util.query(this.socket.options.query, 't=' + +new Date);
+
+ req.open(method || 'GET', this.prepareUrl() + query, true);
+
+ if (method == 'POST') {
+ try {
+ if (req.setRequestHeader) {
+ req.setRequestHeader('Content-type', 'text/plain;charset=UTF-8');
+ } else {
+ // XDomainRequest
+ req.contentType = 'text/plain';
+ }
+ } catch (e) {}
+ }
+
+ return req;
+ };
+
+ /**
+ * Returns the scheme to use for the transport URLs.
+ *
+ * @api private
+ */
+
+ XHR.prototype.scheme = function () {
+ return this.socket.options.secure ? 'https' : 'http';
+ };
+
+ /**
+ * Check if the XHR transports are supported
+ *
+ * @param {Boolean} xdomain Check if we support cross domain requests.
+ * @returns {Boolean}
+ * @api public
+ */
+
+ XHR.check = function (socket, xdomain) {
+ try {
+ var request = io.util.request(xdomain),
+ usesXDomReq = (global.XDomainRequest && request instanceof XDomainRequest),
+ socketProtocol = (socket && socket.options && socket.options.secure ? 'https:' : 'http:'),
+ isXProtocol = (global.location && socketProtocol != global.location.protocol);
+ if (request && !(usesXDomReq && isXProtocol)) {
+ return true;
+ }
+ } catch(e) {}
+
+ return false;
+ };
+
+ /**
+ * Check if the XHR transport supports cross domain requests.
+ *
+ * @returns {Boolean}
+ * @api public
+ */
+
+ XHR.xdomainCheck = function (socket) {
+ return XHR.check(socket, true);
+ };
+
+ })(
+ 'undefined' != typeof io ? io.Transport : module.exports
+ , 'undefined' != typeof io ? io : module.parent.exports
+ , global
+ );
+ /**
+ * socket.io
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+ (function (exports, io) {
+
+ /**
+ * Expose constructor.
+ */
+
+ exports.htmlfile = HTMLFile;
+
+ /**
+ * The HTMLFile transport creates a `forever iframe` based transport
+ * for Internet Explorer. Regular forever iframe implementations will
+ * continuously trigger the browsers buzy indicators. If the forever iframe
+ * is created inside a `htmlfile` these indicators will not be trigged.
+ *
+ * @constructor
+ * @extends {io.Transport.XHR}
+ * @api public
+ */
+
+ function HTMLFile (socket) {
+ io.Transport.XHR.apply(this, arguments);
+ };
+
+ /**
+ * Inherits from XHR transport.
+ */
+
+ io.util.inherit(HTMLFile, io.Transport.XHR);
+
+ /**
+ * Transport name
+ *
+ * @api public
+ */
+
+ HTMLFile.prototype.name = 'htmlfile';
+
+ /**
+ * Creates a new Ac...eX `htmlfile` with a forever loading iframe
+ * that can be used to listen to messages. Inside the generated
+ * `htmlfile` a reference will be made to the HTMLFile transport.
+ *
+ * @api private
+ */
+
+ HTMLFile.prototype.get = function () {
+ this.doc = new window[(['Active'].concat('Object').join('X'))]('htmlfile');
+ this.doc.open();
+ this.doc.write('<html></html>');
+ this.doc.close();
+ this.doc.parentWindow.s = this;
+
+ var iframeC = this.doc.createElement('div');
+ iframeC.className = 'socketio';
+
+ this.doc.body.appendChild(iframeC);
+ this.iframe = this.doc.createElement('iframe');
+
+ iframeC.appendChild(this.iframe);
+
+ var self = this
+ , query = io.util.query(this.socket.options.query, 't='+ +new Date);
+
+ this.iframe.src = this.prepareUrl() + query;
+
+ io.util.on(window, 'unload', function () {
+ self.destroy();
+ });
+ };
+
+ /**
+ * The Socket.IO server will write script tags inside the forever
+ * iframe, this function will be used as callback for the incoming
+ * information.
+ *
+ * @param {String} data The message
+ * @param {document} doc Reference to the context
+ * @api private
+ */
+
+ HTMLFile.prototype._ = function (data, doc) {
+ this.onData(data);
+ try {
+ var script = doc.getElementsByTagName('script')[0];
+ script.parentNode.removeChild(script);
+ } catch (e) { }
+ };
+
+ /**
+ * Destroy the established connection, iframe and `htmlfile`.
+ * And calls the `CollectGarbage` function of Internet Explorer
+ * to release the memory.
+ *
+ * @api private
+ */
+
+ HTMLFile.prototype.destroy = function () {
+ if (this.iframe){
+ try {
+ this.iframe.src = 'about:blank';
+ } catch(e){}
+
+ this.doc = null;
+ this.iframe.parentNode.removeChild(this.iframe);
+ this.iframe = null;
+
+ CollectGarbage();
+ }
+ };
+
+ /**
+ * Disconnects the established connection.
+ *
+ * @returns {Transport} Chaining.
+ * @api public
+ */
+
+ HTMLFile.prototype.close = function () {
+ this.destroy();
+ return io.Transport.XHR.prototype.close.call(this);
+ };
+
+ /**
+ * Checks if the browser supports this transport. The browser
+ * must have an `Ac...eXObject` implementation.
+ *
+ * @return {Boolean}
+ * @api public
+ */
+
+ HTMLFile.check = function (socket) {
+ if (typeof window != "undefined" && (['Active'].concat('Object').join('X')) in window){
+ try {
+ var a = new window[(['Active'].concat('Object').join('X'))]('htmlfile');
+ return a && io.Transport.XHR.check(socket);
+ } catch(e){}
+ }
+ return false;
+ };
+
+ /**
+ * Check if cross domain requests are supported.
+ *
+ * @returns {Boolean}
+ * @api public
+ */
+
+ HTMLFile.xdomainCheck = function () {
+ // we can probably do handling for sub-domains, we should
+ // test that it's cross domain but a subdomain here
+ return false;
+ };
+
+ /**
+ * Add the transport to your public io.transports array.
+ *
+ * @api private
+ */
+
+ io.transports.push('htmlfile');
+
+ })(
+ 'undefined' != typeof io ? io.Transport : module.exports
+ , 'undefined' != typeof io ? io : module.parent.exports
+ );
+
+ /**
+ * socket.io
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+ (function (exports, io, global) {
+
+ /**
+ * Expose constructor.
+ */
+
+ exports['xhr-polling'] = XHRPolling;
+
+ /**
+ * The XHR-polling transport uses long polling XHR requests to create a
+ * "persistent" connection with the server.
+ *
+ * @constructor
+ * @api public
+ */
+
+ function XHRPolling () {
+ io.Transport.XHR.apply(this, arguments);
+ };
+
+ /**
+ * Inherits from XHR transport.
+ */
+
+ io.util.inherit(XHRPolling, io.Transport.XHR);
+
+ /**
+ * Merge the properties from XHR transport
+ */
+
+ io.util.merge(XHRPolling, io.Transport.XHR);
+
+ /**
+ * Transport name
+ *
+ * @api public
+ */
+
+ XHRPolling.prototype.name = 'xhr-polling';
+
+ /**
+ * Indicates whether heartbeats is enabled for this transport
+ *
+ * @api private
+ */
+
+ XHRPolling.prototype.heartbeats = function () {
+ return false;
+ };
+
+ /**
+ * Establish a connection, for iPhone and Android this will be done once the page
+ * is loaded.
+ *
+ * @returns {Transport} Chaining.
+ * @api public
+ */
+
+ XHRPolling.prototype.open = function () {
+ var self = this;
+
+ io.Transport.XHR.prototype.open.call(self);
+ return false;
+ };
+
+ /**
+ * Starts a XHR request to wait for incoming messages.
+ *
+ * @api private
+ */
+
+ function empty () {};
+
+ XHRPolling.prototype.get = function () {
+ if (!this.isOpen) return;
+
+ var self = this;
+
+ function stateChange () {
+ if (this.readyState == 4) {
+ this.onreadystatechange = empty;
+
+ if (this.status == 200) {
+ self.onData(this.responseText);
+ self.get();
+ } else {
+ self.onClose();
+ }
+ }
+ };
+
+ function onload () {
+ this.onload = empty;
+ this.onerror = empty;
+ self.retryCounter = 1;
+ self.onData(this.responseText);
+ self.get();
+ };
+
+ function onerror () {
+ self.retryCounter ++;
+ if(!self.retryCounter || self.retryCounter > 3) {
+ self.onClose();
+ } else {
+ self.get();
+ }
+ };
+
+ this.xhr = this.request();
+
+ if (global.XDomainRequest && this.xhr instanceof XDomainRequest) {
+ this.xhr.onload = onload;
+ this.xhr.onerror = onerror;
+ } else {
+ this.xhr.onreadystatechange = stateChange;
+ }
+
+ this.xhr.send(null);
+ };
+
+ /**
+ * Handle the unclean close behavior.
+ *
+ * @api private
+ */
+
+ XHRPolling.prototype.onClose = function () {
+ io.Transport.XHR.prototype.onClose.call(this);
+
+ if (this.xhr) {
+ this.xhr.onreadystatechange = this.xhr.onload = this.xhr.onerror = empty;
+ try {
+ this.xhr.abort();
+ } catch(e){}
+ this.xhr = null;
+ }
+ };
+
+ /**
+ * Webkit based browsers show a infinit spinner when you start a XHR request
+ * before the browsers onload event is called so we need to defer opening of
+ * the transport until the onload event is called. Wrapping the cb in our
+ * defer method solve this.
+ *
+ * @param {Socket} socket The socket instance that needs a transport
+ * @param {Function} fn The callback
+ * @api private
+ */
+
+ XHRPolling.prototype.ready = function (socket, fn) {
+ var self = this;
+
+ io.util.defer(function () {
+ fn.call(self);
+ });
+ };
+
+ /**
+ * Add the transport to your public io.transports array.
+ *
+ * @api private
+ */
+
+ io.transports.push('xhr-polling');
+
+ })(
+ 'undefined' != typeof io ? io.Transport : module.exports
+ , 'undefined' != typeof io ? io : module.parent.exports
+ , global
+ );
+
+ /**
+ * socket.io
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+ (function (exports, io, global) {
+ /**
+ * There is a way to hide the loading indicator in Firefox. If you create and
+ * remove a iframe it will stop showing the current loading indicator.
+ * Unfortunately we can't feature detect that and UA sniffing is evil.
+ *
+ * @api private
+ */
+
+ var indicator = global.document && "MozAppearance" in
+ global.document.documentElement.style;
+
+ /**
+ * Expose constructor.
+ */
+
+ exports['jsonp-polling'] = JSONPPolling;
+
+ /**
+ * The JSONP transport creates an persistent connection by dynamically
+ * inserting a script tag in the page. This script tag will receive the
+ * information of the Socket.IO server. When new information is received
+ * it creates a new script tag for the new data stream.
+ *
+ * @constructor
+ * @extends {io.Transport.xhr-polling}
+ * @api public
+ */
+
+ function JSONPPolling (socket) {
+ io.Transport['xhr-polling'].apply(this, arguments);
+
+ this.index = io.j.length;
+
+ var self = this;
+
+ io.j.push(function (msg) {
+ self._(msg);
+ });
+ };
+
+ /**
+ * Inherits from XHR polling transport.
+ */
+
+ io.util.inherit(JSONPPolling, io.Transport['xhr-polling']);
+
+ /**
+ * Transport name
+ *
+ * @api public
+ */
+
+ JSONPPolling.prototype.name = 'jsonp-polling';
+
+ /**
+ * Posts a encoded message to the Socket.IO server using an iframe.
+ * The iframe is used because script tags can create POST based requests.
+ * The iframe is positioned outside of the view so the user does not
+ * notice it's existence.
+ *
+ * @param {String} data A encoded message.
+ * @api private
+ */
+
+ JSONPPolling.prototype.post = function (data) {
+ var self = this
+ , query = io.util.query(
+ this.socket.options.query
+ , 't='+ (+new Date) + '&i=' + this.index
+ );
+
+ if (!this.form) {
+ var form = document.createElement('form')
+ , area = document.createElement('textarea')
+ , id = this.iframeId = 'socketio_iframe_' + this.index
+ , iframe;
+
+ form.className = 'socketio';
+ form.style.position = 'absolute';
+ form.style.top = '0px';
+ form.style.left = '0px';
+ form.style.display = 'none';
+ form.target = id;
+ form.method = 'POST';
+ form.setAttribute('accept-charset', 'utf-8');
+ area.name = 'd';
+ form.appendChild(area);
+ document.body.appendChild(form);
+
+ this.form = form;
+ this.area = area;
+ }
+
+ this.form.action = this.prepareUrl() + query;
+
+ function complete () {
+ initIframe();
+ self.socket.setBuffer(false);
+ };
+
+ function initIframe () {
+ if (self.iframe) {
+ self.form.removeChild(self.iframe);
+ }
+
+ try {
+ // ie6 dynamic iframes with target="" support (thanks Chris Lambacher)
+ iframe = document.createElement('<iframe name="'+ self.iframeId +'">');
+ } catch (e) {
+ iframe = document.createElement('iframe');
+ iframe.name = self.iframeId;
+ }
+
+ iframe.id = self.iframeId;
+
+ self.form.appendChild(iframe);
+ self.iframe = iframe;
+ };
+
+ initIframe();
+
+ // we temporarily stringify until we figure out how to prevent
+ // browsers from turning `\n` into `\r\n` in form inputs
+ this.area.value = io.JSON.stringify(data);
+
+ try {
+ this.form.submit();
+ } catch(e) {}
+
+ if (this.iframe.attachEvent) {
+ iframe.onreadystatechange = function () {
+ if (self.iframe.readyState == 'complete') {
+ complete();
+ }
+ };
+ } else {
+ this.iframe.onload = complete;
+ }
+
+ this.socket.setBuffer(true);
+ };
+
+ /**
+ * Creates a new JSONP poll that can be used to listen
+ * for messages from the Socket.IO server.
+ *
+ * @api private
+ */
+
+ JSONPPolling.prototype.get = function () {
+ var self = this
+ , script = document.createElement('script')
+ , query = io.util.query(
+ this.socket.options.query
+ , 't='+ (+new Date) + '&i=' + this.index
+ );
+
+ if (this.script) {
+ this.script.parentNode.removeChild(this.script);
+ this.script = null;
+ }
+
+ script.async = true;
+ script.src = this.prepareUrl() + query;
+ script.onerror = function () {
+ self.onClose();
+ };
+
+ var insertAt = document.getElementsByTagName('script')[0];
+ insertAt.parentNode.insertBefore(script, insertAt);
+ this.script = script;
+
+ if (indicator) {
+ setTimeout(function () {
+ var iframe = document.createElement('iframe');
+ document.body.appendChild(iframe);
+ document.body.removeChild(iframe);
+ }, 100);
+ }
+ };
+
+ /**
+ * Callback function for the incoming message stream from the Socket.IO server.
+ *
+ * @param {String} data The message
+ * @api private
+ */
+
+ JSONPPolling.prototype._ = function (msg) {
+ this.onData(msg);
+ if (this.isOpen) {
+ this.get();
+ }
+ return this;
+ };
+
+ /**
+ * The indicator hack only works after onload
+ *
+ * @param {Socket} socket The socket instance that needs a transport
+ * @param {Function} fn The callback
+ * @api private
+ */
+
+ JSONPPolling.prototype.ready = function (socket, fn) {
+ var self = this;
+ if (!indicator) return fn.call(this);
+
+ io.util.load(function () {
+ fn.call(self);
+ });
+ };
+
+ /**
+ * Checks if browser supports this transport.
+ *
+ * @return {Boolean}
+ * @api public
+ */
+
+ JSONPPolling.check = function () {
+ return 'document' in global;
+ };
+
+ /**
+ * Check if cross domain requests are supported
+ *
+ * @returns {Boolean}
+ * @api public
+ */
+
+ JSONPPolling.xdomainCheck = function () {
+ return true;
+ };
+
+ /**
+ * Add the transport to your public io.transports array.
+ *
+ * @api private
+ */
+
+ io.transports.push('jsonp-polling');
+
+ })(
+ 'undefined' != typeof io ? io.Transport : module.exports
+ , 'undefined' != typeof io ? io : module.parent.exports
+ , global
+ );
+
+ if (typeof define === "function" && define.amd) {
+ define([], function () { return io; });
+ }
+ })();
+
+ /* Notes
+ *
+ * - Continue using prefixed names for now.
+ *
+ */
+
+ var webrtcSupported = true;
+
+ var RTCPeerConnection;
+ if(window.mozRTCPeerConnection)
+ RTCPeerConnection = window.mozRTCPeerConnection;
+ else if(window.webkitRTCPeerConnection)
+ RTCPeerConnection = window.webkitRTCPeerConnection;
+ else if(window.RTCPeerConnection)
+ RTCPeerConnection = window.RTCPeerConnection
+ else
+ webrtcSupported = false;
+
+ var RTCSessionDescription;
+ if(window.mozRTCSessionDescription)
+ RTCSessionDescription = window.mozRTCSessionDescription;
+ else if(window.webkitRTCSessionDescription)
+ RTCSessionDescription = window.webkitRTCSessionDescription;
+ else if(window.RTCSessionDescription)
+ RTCSessionDescription = window.RTCSessionDescription
+ else
+ webrtcSupported = false;
+
+ var RTCIceCandidate;
+ if(window.mozRTCIceCandidate)
+ RTCIceCandidate = window.mozRTCIceCandidate;
+ else if(window.webkitRTCIceCandidate)
+ RTCIceCandidate = window.webkitRTCIceCandidate;
+ else if(window.RTCIceCandidate)
+ RTCIceCandidate = window.RTCIceCandidate;
+ else
+ webrtcSupported = false;
+
+ var getUserMedia;
+ if(!navigator.getUserMedia) {
+ if(navigator.mozGetUserMedia)
+ getUserMedia = navigator.mozGetUserMedia.bind(navigator);
+ else if(navigator.webkitGetUserMedia)
+ getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
+ else
+ webrtcSupported = false;
+ } else {
+ getUserMedia = navigator.getUserMedia.bind(navigator);
+ }
+
+ // FIXME: browser detection is gross, but I don't see another way to do this
+ var RTCConnectProtocol;
+ if(window.mozRTCPeerConnection) {
+ RTCConnectProtocol = mozRTCConnectProtocol;
+ } else if(window.webkitRTCPeerConnection) {
+ RTCConnectProtocol = webkitRTCConnectProtocol;
+ } else {
+ webrtcSupported = false;
+ }
+
+ function callback(object, method, args) {
+ if(!Array.isArray(args))
+ args = [args];
+ if(method in object && 'function' === typeof object[method]) {
+ object[method].apply(object, args);
+ }
+ };
+
+ function fail(object, method, error) {
+ if (!(error instanceof Error))
+ error = new Error(error);
+ callback(object, method, [error]);
+ };
+
+ function defer(queue, object, method, args) {
+ if(queue) {
+ queue.push([object, method, args]);
+ return true;
+ } else {
+ return false;
+ }
+ };
+
+ function processDeferredQueue(queue) {
+ while(queue.length) {
+ var deferred = queue.shift();
+ callback(deferred[0], deferred[1], deferred[2]);
+ }
+ };
+
+ var ONE_SECOND = 1000; // milliseconds
+ var DEFAULT_CONNECTION_TIMEOUT = 10 * ONE_SECOND;
+ var DEFAULT_PING_TIMEOUT = 1 * ONE_SECOND;
+ var RELIABLE_CHANNEL_OPTIONS = {
+ reliable: false
+ };
+ var UNRELIABLE_CHANNEL_OPTIONS = {
+ outOfOrderAllowed: true,
+ maxRetransmitNum: 0,
+ reliable: false
+ };
+
+ function PendingConnectionAbortError(message) {
+ this.name = "PendingConnectionAbortError";
+ this.message = (message || "");
+ };
+ PendingConnectionAbortError.prototype = Error.prototype;
+
+ function ConnectionFailedError(message) {
+ this.name = "ConnectionFailedError";
+ this.message = (message || "");
+ };
+ ConnectionFailedError.prototype = Error.prototype;
+
+ var E = {
+ PendingConnectionAbortError: PendingConnectionAbortError,
+ ConnectionFailedError: ConnectionFailedError
+ };
+
+ function WebSocketBroker(brokerUrl) {
+ this.brokerUrl = brokerUrl;
+ this.state = WebSocketBroker.OFFLINE;
+
+ this.onstatechange = null;
+ this.onreceive = null;
+ this.onerror = null;
+
+ this.socket = null;
+ this.route = null;
+ };
+
+ // States
+ WebSocketBroker.OFFLINE = 0x01;
+ WebSocketBroker.CONNECTING = 0x02;
+ WebSocketBroker.CONNECTED = 0x04;
+ // Flags
+ WebSocketBroker.ROUTED = 0x10;
+ WebSocketBroker.LISTENING = 0x20;
+
+ WebSocketBroker.prototype.setState = function setState(state, clearFlags) {
+ var clear = clearFlags ? 0x00 : 0xF0;
+ this.state &= clear >>> 0;
+ this.state |= state >>> 0;
+ callback(this, 'onstatechange', [this.state, (state | (clear & 0x0)) >>> 0]);
+ };
+ WebSocketBroker.prototype.setFlag = function setFlag(flag) {
+ this.state = (this.state | flag) >>> 0;
+ callback(this, 'onstatechange', [this.state, flag])
+ };
+ WebSocketBroker.prototype.clearFlag = function clearFlag(flag) {
+ flag = (~flag) >>> 0;
+ this.state = (this.state & flag) >>> 0;
+ callback(this, 'onstatechange', [this.state, flag])
+ };
+ WebSocketBroker.prototype.checkState = function checkState(mask) {
+ return !!(this.state & mask);
+ };
+ WebSocketBroker.prototype.connect = function connect() {
+ var that = this;
+ var socket = io.connect(this.brokerUrl + '/peer', {
+ 'sync disconnect on unload': true // partially fixes 'interrupted while page loading' warning
+ });
+
+ socket.on('connecting', function onconnecting() {
+ that.setState(WebSocketBroker.CONNECTING, true);
+ });
+
+ socket.on('connect', function onconnect() {
+ that.setState(WebSocketBroker.CONNECTED, true);
+ });
+
+ socket.on('connect_failed', function onconnect_failed() {
+ that.setState(WebSocketBroker.OFFLINE, true);
+ });
+
+ socket.on('route', function onroute(route) {
+ that.route = route;
+ that.setFlag(WebSocketBroker.ROUTED);
+ });
+
+ socket.on('disconnect', function ondisconnect() {
+ that.setState(WebSocketBroker.OFFLINE, true);
+ });
+
+ socket.on('error', function onerror(error) {
+ console.error(error);
+ fail(that, 'onerror', error);
+ });
+
+ socket.on('receive', function onreceive(message) {
+ var from = message['from'];
+ var data = message['data'];
+ callback(that, 'onreceive', [from, data]);
+ });
+
+ this.socket = socket;
+ };
+ WebSocketBroker.prototype.disconnect = function disconnect() {
+ if(this.checkState(WebSocketBroker.CONNECTED)) {
+ this.socket.disconnect();
+ this.setState(WebSocketBroker.OFFLINE, true);
+ return true;
+ } else {
+ return false;
+ }
+ };
+ WebSocketBroker.prototype.listen = function listen(options) {
+ var that = this;
+ if(this.checkState(WebSocketBroker.CONNECTED)) {
+ this.socket.emit('listen', options, function onresponse(response) {
+ if(response && response['error']) {
+ var error = new Error(response['error']);
+ fail(that, 'onerror', error);
+ } else {
+ that.setFlag(WebSocketBroker.LISTENING);
+ }
+ });
+ }
+ };
+ WebSocketBroker.prototype.ignore = function ignore() {
+ var that = this;
+ if(this.checkState(WebSocketBroker.CONNECTED)) {
+ this.socket.emit('ignore', null, function onresponse(response) {
+ if(response && response['error']) {
+ var error = new Error(response['error']);
+ fail(that, 'onerror', error)
+ } else {
+ that.clearFlag(WebSocketBroker.LISTENING);
+ }
+ });
+ }
+ };
+ WebSocketBroker.prototype.send = function send(to, message) {
+ var that = this;
+ if(this.checkState(WebSocketBroker.CONNECTED)) {
+ this.socket.emit('send', {'to': to, 'data': message}, function onresponse(response) {
+ if(response && response['error']) {
+ var error = new Error(response['error']);
+ fail(that, 'onerror', error)
+ }
+ });
+ };
+ };
+
+ var dataChannels = {
+ 'reliable': 'RELIABLE',
+ 'unreliable': 'UNRELIABLE',
+ '@control': 'RELIABLE'
+ };
+ var nextDataConnectionPort = 1;
+ function CommonRTCConnectProtocol() {
+ // FIXME: these timeouts should be configurable
+ this.connectionTimeout = 10 * ONE_SECOND;
+ this.pingTimeout = 1 * ONE_SECOND;
+ };
+ CommonRTCConnectProtocol.prototype.process = function process(message) {
+ var that = this;
+
+ var type = message['type'];
+ switch(type) {
+ case 'ice':
+ var candidate = JSON.parse(message['candidate']);
+ if(candidate)
+ this.handleIce(candidate);
+ break;
+
+ case 'offer':
+ that.ports.remote = message['port'];
+ var offer = {
+ 'type': 'offer',
+ 'sdp': message['description']
+ };
+ this.handleOffer(offer);
+ break;
+
+ case 'answer':
+ that.ports.remote = message['port'];
+ var answer = {
+ 'type': 'answer',
+ 'sdp': message['description']
+ };
+ this.handleAnswer(answer);
+ break;
+
+ case 'abort':
+ this.handleAbort();
+ break;
+
+ default:
+ fail(this, 'onerror', 'unknown message');
+ }
+ };
+ CommonRTCConnectProtocol.prototype.handleAbort = function handleAbort() {
+ fail(this, 'onerror', new Error(E.RTCConnectProtocolAbort));
+ };
+ CommonRTCConnectProtocol.prototype.initialize = function initialize(cb) {
+ var that = this;
+
+ if(this.peerConnection)
+ return cb();
+
+ // FIXME: peer connection servers should be configurable
+ this.peerConnection = new RTCPeerConnection(this.connectionServers, this.connectionOptions);
+ this.peerConnection.onicecandidate = function(event) {
+ var message = {
+ 'type': 'ice',
+ 'candidate': JSON.stringify(event.candidate)
+ };
+ callback(that, 'onmessage', message);
+ };
+ this.peerConnection.onaddstream = function(event) {
+ that.streams['remote'] = event.stream;
+ };
+ this.peerConnection.onstatechange = function(event) {
+ console.log(event.target.readyState);
+ };
+
+ function createStream(useFake) {
+ useFake = (!useVideo && !useAudio) ? true : useFake;
+ var useVideo = !!that.options['video'];
+ var useAudio = !!that.options['audio'];
+ var mediaOptions = {
+ video: useVideo,
+ audio: (!useVideo && !useAudio) ? true : useAudio,
+ fake: useFake
+ };
+ getUserMedia(mediaOptions,
+ function(stream) {
+ that.peerConnection.addStream(stream);
+ that.streams['local'] = stream;
+ cb();
+ },
+ function(error) {
+ console.error('!', error);
+ if(!useFake)
+ createStream(true);
+ else
+ fail(that, 'onerror', error);
+ }
+ );
+ }
+
+ createStream();
+ };
+ CommonRTCConnectProtocol.prototype.handleIce = function handleIce(candidate) {
+ var that = this;
+
+ function setIce() {
+ if(!that.peerConnection.remoteDescription) {
+ return
+ }
+ that.peerConnection.addIceCandidate(new RTCIceCandidate(candidate),
+ function(error) {
+ fail(that, 'onerror', error);
+ }
+ );
+ };
+
+ this.initialize(setIce);
+ };
+ CommonRTCConnectProtocol.prototype.initiate = function initiate() {
+ var that = this;
+ this.initiator = true;
+
+ function createDataChannels() {
+ var labels = Object.keys(dataChannels);
+ labels.forEach(function(label) {
+ var channelOptions = that.channelOptions[dataChannels[label]];
+ var channel = that._pending[label] = that.peerConnection.createDataChannel(label, channelOptions);
+ channel.binaryType = that.options['binaryType'];
+ channel.onopen = function() {
+ that.channels[label] = channel;
+ delete that._pending[label];
+ if(Object.keys(that.channels).length === labels.length) {
+ that.complete = true;
+ callback(that, 'oncomplete', []);
+ }
+ };
+ channel.onerror = function(error) {
+ console.error(error);
+ fail(that, 'onerror', error);
+ };
+ });
+ createOffer();
+ };
+
+ function createOffer() {
+ that.peerConnection.createOffer(setLocal,
+ function(error) {
+ fail(that, 'onerror', error);
+ }
+ );
+ };
+
+ function setLocal(description) {
+ that.peerConnection.setLocalDescription(new RTCSessionDescription(description), complete,
+ function(error) {
+ fail(that, 'onerror', error);
+ }
+ );
+
+ function complete() {
+ var message = {
+ 'type': 'offer',
+ 'description': description['sdp'],
+ 'port': that.ports.local
+ };
+ callback(that, 'onmessage', message);
+ };
+ };
+
+ this.initialize(createDataChannels);
+ };
+ CommonRTCConnectProtocol.prototype.handleOffer = function handleOffer(offer) {
+ var that = this;
+
+ function handleDataChannels() {
+ var labels = Object.keys(dataChannels);
+ that.peerConnection.ondatachannel = function(event) {
+ var channel = event.channel;
+ var label = channel.label;
+ that._pending[label] = channel;
+ channel.binaryType = that.options['binaryType'];
+ channel.onopen = function() {
+ that.channels[label] = channel;
+ delete that._pending[label];
+ if(Object.keys(that.channels).length === labels.length) {
+ that.complete = true;
+ callback(that, 'oncomplete', []);
+ }
+ };
+ channel.onerror = function(error) {
+ console.error(error);
+ fail(that, 'onerror', error);
+ };
+ };
+ setRemote();
+ };
+
+ function setRemote() {
+ that.peerConnection.setRemoteDescription(new RTCSessionDescription(offer), createAnswer,
+ function(error) {
+ fail(that, 'onerror', error);
+ }
+ );
+ };
+
+ function createAnswer() {
+ that.peerConnection.createAnswer(setLocal,
+ function(error) {
+ fail(that, 'onerror', error);
+ }
+ );
+ };
+
+ function setLocal(description) {
+ that.peerConnection.setLocalDescription(new RTCSessionDescription(description), complete,
+ function(error) {
+ fail(that, 'onerror', error);
+ }
+ );
+
+ function complete() {
+ var message = {
+ 'type': 'answer',
+ 'description': description['sdp'],
+ 'port': that.ports.local
+ };
+ callback(that, 'onmessage', message);
+ };
+ };
+
+ this.initialize(handleDataChannels);
+ };
+ CommonRTCConnectProtocol.prototype.handleAnswer = function handleAnswer(answer) {
+ var that = this;
+
+ function setRemote() {
+ that.peerConnection.setRemoteDescription(new RTCSessionDescription(answer), complete,
+ function(error) {
+ fail(that, 'onerror', error);
+ }
+ );
+ };
+
+ function complete() {
+ };
+
+ this.initialize(setRemote);
+ };
+
+ function mozRTCConnectProtocol(options) {
+ this.options = options;
+ this.onmessage = null;
+ this.oncomplete = null;
+ this.onerror = null;
+
+ this.complete = false;
+ this.ports = {
+ local: nextDataConnectionPort ++,
+ remote: null
+ };
+ this.streams = {
+ local: null,
+ remote: null
+ };
+ this.initiator = false;
+
+ this.peerConnection = null;
+ this.channels = {};
+ this._pending = {};
+ this.connectionServers = null;
+ this.connectionOptions = null;
+ this.channelOptions = {
+ RELIABLE: {
+ // defaults
+ },
+ UNRELIABLE: {
+ outOfOrderAllowed: true,
+ maxRetransmitNum: 0
+ }
+ };
+ };
+ mozRTCConnectProtocol.prototype = new CommonRTCConnectProtocol();
+ mozRTCConnectProtocol.prototype.constructor = mozRTCConnectProtocol;
+
+ function webkitRTCConnectProtocol(options) {
+ this.options = options;
+ this.onmessage = null;
+ this.oncomplete = null;
+ this.onerror = null;
+
+ this.complete = false;
+ this.ports = {
+ local: nextDataConnectionPort ++,
+ remote: null
+ };
+ this.streams = {
+ local: null,
+ remote: null
+ };
+ this.initiator = false;
+
+ this.peerConnection = null;
+ this.channels = {};
+ this._pending = {};
+ this.connectionServers = {iceServers:[{url:'stun:23.21.150.121'}]};
+ this.connectionOptions = {
+ 'optional': [{ 'RtpDataChannels': true }]
+ };
+ this.channelOptions = {
+ RELIABLE: {
+ // FIXME: reliable channels do not work in chrome yet
+ reliable: false
+ },
+ UNRELIABLE: {
+ reliable: false
+ }
+ };
+ };
+ webkitRTCConnectProtocol.prototype = new CommonRTCConnectProtocol();
+ webkitRTCConnectProtocol.prototype.constructor = webkitRTCConnectProtocol;
+
+ // FIXME: this could use a cleanup
+ var nextConnectionId = 1;
+ function Connection(options, peerConnection, streams, channels) {
+ var that = this;
+ this.id = nextConnectionId ++;
+ this.streams = streams;
+ this.connected = false;
+ this.messageFlag = false;
+
+ this.onmessage = null;
+ this.ondisconnect = null;
+ this.onerror = null;
+
+ this.peerConnection = peerConnection;
+
+ // DataChannels
+ this.channels = channels;
+
+ this.connectionTimer = null;
+ this.pingTimer = null;
+
+ function handleConnectionTimerExpired() {
+ if(!that.connected)
+ return
+ this.connectionTimer = null;
+ if(false === that.messageFlag) {
+ that.channels['@control'].send('ping');
+ this.pingTimer = window.setTimeout(handlePingTimerExpired, options['pingTimeout']);
+ } else {
+ that.messageFlag = false;
+ this.connectionTimer = window.setTimeout(handleConnectionTimerExpired, options['connectionTimeout']);
+ }
+ };
+ function handlePingTimerExpired() {
+ if(!that.connected)
+ return
+ this.pingTimer = null;
+ if(false === that.messageFlag) {
+ that.connected = false;
+ that.close();
+ } else {
+ that.messageFlag = false;
+ this.connectionTimer = window.setTimeout(handleConnectionTimerExpired, options['connectionTimeout']);
+ }
+ };
+
+ Object.keys(this.channels).forEach(function(label) {
+ var channel = that.channels[label];
+ if(label.match('^@')) // check for internal channels
+ return;
+
+ channel.onmessage = function onmessage(message) {
+ that.messageFlag = true;
+ callback(that, 'onmessage', [label, message]);
+ };
+ });
+ this.channels['@control'].onmessage = function onmessage(message) {
+ that.messageFlag = true;
+ if(that.connected) {
+ var data = message.data;
+ if('ping' === data) {
+ that.channels['@control'].send('pong');
+ } else if('pong' === data) {
+ // ok
+ } else if('quit' === data) {
+ that.close();
+ }
+ }
+ };
+
+ this.connected = true;
+ this.connectionTimer = window.setTimeout(handleConnectionTimerExpired, options['connectionTimeout']);
+ };
+ Connection.prototype.close = function close() {
+ console.log('close connection');
+ if(this.connected) {
+ this.channels['@control'].send('quit');
+ }
+ this.connected = false;
+ this.peerConnection.close();
+ if(this.connectionTimer) {
+ window.clearInterval(this.connectionTimer);
+ this.connectionTimer = null;
+ }
+ if(this.pingTimer) {
+ window.clearInterval(this.pingTimer);
+ this.pingTimer = null;
+ }
+ this.peerConnection = null;
+ callback(this, 'ondisconnect', []);
+ };
+ Connection.prototype.send = function send(label, message) {
+ this.channels[label].send(message);
+ };
+
+ function PendingConnection(route, incoming) {
+ this.route = route;
+ this.incoming = incoming;
+ this.proceed = true;
+ };
+ PendingConnection.prototype.accept = function accept() {
+ this.proceed = true;
+ };
+ PendingConnection.prototype.reject = function reject() {
+ this.proceed = false;
+ };
+
+ function Peer(brokerUrl, options) {
+ if(!webrtcSupported)
+ throw new Error("WebRTC not supported");
+
+ var that = this;
+ this.brokerUrl = brokerUrl;
+ this.options = options = options || {};
+ options['binaryType'] = options['binaryType'] || 'arraybuffer';
+ options['connectionTimeout'] = options['connectionTimeout'] || 10 * ONE_SECOND;
+ options['pingTimeout'] = options['pingTimeout'] || 1 * ONE_SECOND;
+
+ this.onconnection = null;
+ this.onpending = null;
+ this.onroute = null;
+ this.onerror = null;
+
+ this.broker = new WebSocketBroker(brokerUrl);
+ this.broker.onerror = function(error) {
+ fail(that, 'onerror', error);
+ };
+ this.pending = {};
+
+ this.queues = {
+ connected: [],
+ listening: []
+ };
+
+ this.broker.onstatechange = function onstatechange(state, mask) {
+ if(that.queues.connected.length && that.broker.checkState(WebSocketBroker.ROUTED)) {
+ processDeferredQueue(that.queues.connected);
+ if(that.queues.listening.length && that.broker.checkState(WebSocketBroker.LISTENING)) {
+ processDeferredQueue(that.queues.listening);
+ }
+ }
+ if(mask & WebSocketBroker.ROUTED) {
+ callback(that, 'onroute', that.broker.route);
+ }
+ };
+
+ this.broker.onreceive = function onreceive(from, message) {
+ var handshake;
+ if(!that.pending.hasOwnProperty(from)) {
+ if(!that.broker.checkState(WebSocketBroker.LISTENING)) {
+ return;
+ }
+
+ var pendingConnection = new PendingConnection(from, /*incoming*/ true);
+ callback(that, 'onpending', [pendingConnection]);
+ if(!pendingConnection['proceed'])
+ return;
+
+ var handshake = that.pending[from] = new RTCConnectProtocol(that.options);
+ handshake.oncomplete = function() {
+ var connection = new Connection(that.options, handshake.peerConnection, handshake.streams, handshake.channels);
+ connection['route'] = from;
+ delete that.pending[from];
+ callback(that, 'onconnection', [connection]);
+ };
+ handshake.onmessage = function(message) {
+ that.broker.send(from, message);
+ };
+ handshake.onerror = function(error) {
+ delete that.pending[from];
+ callback(that, 'onerror', [error]);
+ };
+ } else {
+ handshake = that.pending[from];
+ }
+ handshake.process(message);
+ };
+
+ this.broker.connect();
+ };
+ Peer.prototype.listen = function listen(options) {
+ if(!this.broker.checkState(WebSocketBroker.ROUTED))
+ return defer(this.queues.connected, this, 'listen', [options]);
+
+ options = options || {};
+ options['url'] = options['url'] || window.location.toString();
+ options['listed'] = (undefined !== options['listed']) ? options['listed'] : true;
+ options['metadata'] = options['metadata'] || {};
+
+ this.broker.listen(options);
+ };
+ Peer.prototype.ignore = function ignore() {
+ throw new Error('not implemented');
+ };
+ Peer.prototype.connect = function connect(route) {
+ if(!this.broker.checkState(WebSocketBroker.ROUTED))
+ return defer(this.queues.connected, this, 'connect', [route]);
+
+ var that = this;
+
+ if(this.pending.hasOwnProperty(route))
+ throw new Error('already connecting to this host'); // FIXME: we can handle this better
+
+ var pendingConnection = new PendingConnection(route, /*incoming*/ false);
+ callback(that, 'onpending', [pendingConnection]);
+ if(!pendingConnection['proceed'])
+ return;
+
+ var handshake = this.pending[route] = new RTCConnectProtocol(this.options);
+ handshake.oncomplete = function() {
+ var connection = new Connection(this.options, handshake.peerConnection, handshake.streams, handshake.channels);
+ connection['route'] = route;
+ delete that.pending[route];
+ callback(that, 'onconnection', [connection]);
+ };
+ handshake.onmessage = function(message) {
+ that.broker.send(route, message);
+ };
+ handshake.onerror = function(error) {
+ delete that.pending[route];
+ fail(that, 'onerror', error);
+ };
+
+ handshake.initiate();
+ };
+ Peer.prototype.close = function close() {
+ this.broker.disconnect();
+ };
+ Peer.E = E;
+
+ return Peer;
+
+});
+})(typeof define == 'function' && define.amd
+? define
+: function (deps, factory) { typeof exports === 'object'
+? (module.exports = factory())
+: (this.Peer = factory());
+},
+// Boilerplate for AMD, Node, and browser global
+this
+); \ No newline at end of file
diff --git a/tests/sockets/p2p/package.json b/tests/sockets/p2p/package.json
new file mode 100644
index 00000000..e94eef45
--- /dev/null
+++ b/tests/sockets/p2p/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "p2p",
+ "version": "0.0.1-21",
+ "private": true,
+ "scripts": {
+ "start": "node broker/p2p-broker.js"
+ },
+ "dependencies": {
+ "lodash": "~1.0.1",
+ "socket.io": "~0.9.13"
+ },
+ "engines": {
+ "node": "0.8.x",
+ "npm": "1.1.x"
+ },
+ "subdomain": "wrtcb"
+}
diff --git a/tests/sockets/webrtc_host.c b/tests/sockets/webrtc_host.c
index e482d4ae..38adb0f2 100644
--- a/tests/sockets/webrtc_host.c
+++ b/tests/sockets/webrtc_host.c
@@ -22,7 +22,7 @@ char buf[BUFLEN];
char expected[] = "emscripten";
struct sockaddr_in si_host,
si_peer;
-struct iovec iov[1];
+struct iovec iov[1];
struct msghdr hdr;
int done = 0;
@@ -38,7 +38,10 @@ void iter() {
close(sock);
#ifdef __EMSCRIPTEN__
- int result = 1;
+ if(strlen((char*)hdr.msg_iov[0].iov_base) == strlen(expected) &&
+ 0 == strncmp((char*)hdr.msg_iov[0].iov_base, expected, strlen(expected))) {
+ result = 1;
+ }
REPORT_RESULT();
exit(EXIT_SUCCESS);
emscripten_cancel_main_loop();
@@ -68,10 +71,10 @@ int main(void)
perror("cannot bind host socket");
exit(EXIT_FAILURE);
}
-
+
iov[0].iov_base = buf;
iov[0].iov_len = sizeof(buf);
-
+
memset (&hdr, 0, sizeof (struct msghdr));
hdr.msg_name = &si_peer;
diff --git a/tests/sockets/webrtc_peer.c b/tests/sockets/webrtc_peer.c
index 327782d6..7b5f3a8a 100644
--- a/tests/sockets/webrtc_peer.c
+++ b/tests/sockets/webrtc_peer.c
@@ -46,7 +46,7 @@ void iter() {
}
int main(void)
-{
+{
memset(&si_host, 0, sizeof(struct sockaddr_in));
si_host.sin_family = AF_INET;
@@ -63,7 +63,7 @@ int main(void)
iov[0].iov_base = buf;
iov[0].iov_len = sizeof(buf);
-
+
memset (&hdr, 0, sizeof (struct msghdr));
hdr.msg_name = &si_host;
diff --git a/tests/test_benchmark.py b/tests/test_benchmark.py
index 2bc34c60..4c39a764 100644
--- a/tests/test_benchmark.py
+++ b/tests/test_benchmark.py
@@ -20,9 +20,10 @@ class Benchmarker:
def __init__(self, name):
self.name = name
- def bench(self, args, output_parser=None):
+ def bench(self, args, output_parser=None, reps=TEST_REPS):
self.times = []
- for i in range(TEST_REPS):
+ self.reps = reps
+ for i in range(reps):
start = time.time()
output = self.run(args)
if not output_parser:
@@ -41,7 +42,7 @@ class Benchmarker:
sorted_times.sort()
median = sum(sorted_times[len(sorted_times)/2 - 1:len(sorted_times)/2 + 1])/2
- print ' %10s: mean: %4.3f (+-%4.3f) secs median: %4.3f range: %4.3f-%4.3f (noise: %4.3f%%) (%d runs)' % (self.name, mean, std, median, min(self.times), max(self.times), 100*std/mean, TEST_REPS),
+ print ' %10s: mean: %4.3f (+-%4.3f) secs median: %4.3f range: %4.3f-%4.3f (noise: %4.3f%%) (%d runs)' % (self.name, mean, std, median, min(self.times), max(self.times), 100*std/mean, self.reps),
if baseline:
mean_baseline = sum(baseline.times)/len(baseline.times)
@@ -130,6 +131,7 @@ try:
#NativeBenchmarker('clang-3.4', os.path.join(LLVM_3_4, 'clang'), os.path.join(LLVM_3_4, 'clang++')),
#NativeBenchmarker('gcc', 'gcc', 'g++'),
JSBenchmarker('sm-f32', SPIDERMONKEY_ENGINE, ['-s', 'PRECISE_F32=2']),
+ #JSBenchmarker('sm-f32-si', SPIDERMONKEY_ENGINE, ['-profiling', '-s', 'PRECISE_F32=2', '-s', 'SIMPLIFY_IFS=1']),
#JSBenchmarker('sm-f32-aggro', SPIDERMONKEY_ENGINE, ['-s', 'PRECISE_F32=2', '-s', 'AGGRESSIVE_VARIABLE_ELIMINATION=1']),
#JSBenchmarker('sm-f32-3.2', SPIDERMONKEY_ENGINE, ['-s', 'PRECISE_F32=2'], env={ 'LLVM': LLVM_3_2 }),
#JSBenchmarker('sm-f32-3.3', SPIDERMONKEY_ENGINE, ['-s', 'PRECISE_F32=2'], env={ 'LLVM': LLVM_3_3 }),
@@ -192,7 +194,7 @@ class benchmark(RunnerCore):
print
for b in benchmarkers:
b.build(self, filename, args, shared_args, emcc_args, native_args, native_exec, lib_builder)
- b.bench(args, output_parser)
+ b.bench(args, output_parser, reps)
b.display(benchmarkers[0])
def test_primes(self):
@@ -361,6 +363,51 @@ class benchmark(RunnerCore):
'''
self.do_benchmark('copy', src, 'sum:')
+ def test_ifs(self):
+ src = r'''
+ #include <stdio.h>
+ #include <stdlib.h>
+
+ volatile int x = 0;
+
+ __attribute__ ((noinline)) int calc() {
+ return (x++) & 16384;
+ }
+
+ int main(int argc, char *argv[]) {
+ int arg = argc > 1 ? argv[1][0] - '0' : 3;
+ switch(arg) {
+ case 0: return 0; break;
+ case 1: arg = 75; break;
+ case 2: arg = 625; break;
+ case 3: arg = 1250; break;
+ case 4: arg = 5*1250; break;
+ case 5: arg = 10*1250; break;
+ default: printf("error: %d\\n", arg); return -1;
+ }
+
+ int sum = 0;
+
+ for (int j = 0; j < 27000; j++) {
+ for (int i = 0; i < arg; i++) {
+ if (calc() && calc()) {
+ sum += 17;
+ } else {
+ sum += 19;
+ }
+ if (calc() || calc()) {
+ sum += 23;
+ }
+ }
+ }
+
+ printf("ok\n");
+
+ return sum;
+ }
+ '''
+ self.do_benchmark('ifs', src, 'ok', reps=TEST_REPS*5)
+
def test_fannkuch(self):
src = open(path_from_root('tests', 'fannkuch.cpp'), 'r').read().replace(
'int n = argc > 1 ? atoi(argv[1]) : 0;',
diff --git a/tests/test_browser.py b/tests/test_browser.py
index dfb1ef69..f0343669 100644
--- a/tests/test_browser.py
+++ b/tests/test_browser.py
@@ -51,23 +51,6 @@ def test_chunked_synchronous_xhr_server(support_byte_ranges, chunkSize, data, ch
httpd.handle_request()
class browser(BrowserCore):
- @staticmethod
- def audio():
- print
- print 'Running the browser audio tests. Make sure to listen to hear the correct results!'
- print
- audio_test_cases = [
- 'test_sdl_audio',
- 'test_sdl_audio_mix_channels',
- 'test_sdl_audio_mix',
- 'test_sdl_audio_quickload',
- 'test_sdl_audio_beeps',
- 'test_openal_playback',
- 'test_openal_buffers',
- 'test_freealut'
- ]
- return unittest.TestSuite(map(browser, audio_test_cases))
-
@classmethod
def setUpClass(self):
super(browser, self).setUpClass()
@@ -1130,47 +1113,6 @@ keydown(100);keyup(100); // trigger the end
''')
self.btest('sdl_pumpevents.c', expected='7', args=['--pre-js', 'pre.js'])
- def test_sdl_audio(self):
- shutil.copyfile(path_from_root('tests', 'sounds', 'alarmvictory_1.ogg'), os.path.join(self.get_dir(), 'sound.ogg'))
- shutil.copyfile(path_from_root('tests', 'sounds', 'alarmcreatemiltaryfoot_1.wav'), os.path.join(self.get_dir(), 'sound2.wav'))
- shutil.copyfile(path_from_root('tests', 'sounds', 'noise.ogg'), os.path.join(self.get_dir(), 'noise.ogg'))
- shutil.copyfile(path_from_root('tests', 'sounds', 'the_entertainer.ogg'), os.path.join(self.get_dir(), 'the_entertainer.ogg'))
- open(os.path.join(self.get_dir(), 'bad.ogg'), 'w').write('I claim to be audio, but am lying')
- open(os.path.join(self.get_dir(), 'sdl_audio.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio.c')).read()))
-
- # use closure to check for a possible bug with closure minifying away newer Audio() attributes
- Popen([PYTHON, EMCC, '-O2', '--closure', '1', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio.c'), '--preload-file', 'sound.ogg', '--preload-file', 'sound2.wav', '--embed-file', 'the_entertainer.ogg', '--preload-file', 'noise.ogg', '--preload-file', 'bad.ogg', '-o', 'page.html', '-s', 'EXPORTED_FUNCTIONS=["_main", "_play", "_play2"]']).communicate()
- self.run_browser('page.html', '', '/report_result?1')
-
- def test_sdl_audio_mix_channels(self):
- shutil.copyfile(path_from_root('tests', 'sounds', 'noise.ogg'), os.path.join(self.get_dir(), 'sound.ogg'))
- open(os.path.join(self.get_dir(), 'sdl_audio_mix_channels.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio_mix_channels.c')).read()))
-
- Popen([PYTHON, EMCC, '-O2', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio_mix_channels.c'), '--preload-file', 'sound.ogg', '-o', 'page.html']).communicate()
- self.run_browser('page.html', '', '/report_result?1')
-
- def test_sdl_audio_mix(self):
- shutil.copyfile(path_from_root('tests', 'sounds', 'pluck.ogg'), os.path.join(self.get_dir(), 'sound.ogg'))
- shutil.copyfile(path_from_root('tests', 'sounds', 'the_entertainer.ogg'), os.path.join(self.get_dir(), 'music.ogg'))
- shutil.copyfile(path_from_root('tests', 'sounds', 'noise.ogg'), os.path.join(self.get_dir(), 'noise.ogg'))
- open(os.path.join(self.get_dir(), 'sdl_audio_mix.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio_mix.c')).read()))
-
- Popen([PYTHON, EMCC, '-O2', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio_mix.c'), '--preload-file', 'sound.ogg', '--preload-file', 'music.ogg', '--preload-file', 'noise.ogg', '-o', 'page.html']).communicate()
- self.run_browser('page.html', '', '/report_result?1')
-
- def test_sdl_audio_quickload(self):
- open(os.path.join(self.get_dir(), 'sdl_audio_quickload.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio_quickload.c')).read()))
-
- Popen([PYTHON, EMCC, '-O2', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio_quickload.c'), '-o', 'page.html', '-s', 'EXPORTED_FUNCTIONS=["_main", "_play"]']).communicate()
- self.run_browser('page.html', '', '/report_result?1')
-
- def test_sdl_audio_beeps(self):
- open(os.path.join(self.get_dir(), 'sdl_audio_beep.cpp'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio_beep.cpp')).read()))
-
- # use closure to check for a possible bug with closure minifying away newer Audio() attributes
- Popen([PYTHON, EMCC, '-O2', '--closure', '1', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio_beep.cpp'), '-s', 'DISABLE_EXCEPTION_CATCHING=0', '-o', 'page.html']).communicate()
- self.run_browser('page.html', '', '/report_result?1')
-
def test_sdl_canvas_size(self):
self.btest('sdl_canvas_size.c', reference='screenshot-gray-purple.png', reference_slack=1,
args=['-O2', '--minify', '0', '--shell-file', path_from_root('tests', 'sdl_canvas_size.html'), '--preload-file', path_from_root('tests', 'screenshot.png') + '@/', '-s', 'LEGACY_GL_EMULATION=1'],
@@ -1236,17 +1178,6 @@ keydown(100);keyup(100); // trigger the end
args=['--preload-file', 'screenshot.png', '-s', 'LEGACY_GL_EMULATION=1'],
message='You should see an image with fog.')
- def test_openal_playback(self):
- shutil.copyfile(path_from_root('tests', 'sounds', 'audio.wav'), os.path.join(self.get_dir(), 'audio.wav'))
- open(os.path.join(self.get_dir(), 'openal_playback.cpp'), 'w').write(self.with_report_result(open(path_from_root('tests', 'openal_playback.cpp')).read()))
-
- Popen([PYTHON, EMCC, '-O2', os.path.join(self.get_dir(), 'openal_playback.cpp'), '--preload-file', 'audio.wav', '-o', 'page.html']).communicate()
- self.run_browser('page.html', '', '/report_result?1')
-
- def test_openal_buffers(self):
- shutil.copyfile(path_from_root('tests', 'sounds', 'the_entertainer.wav'), os.path.join(self.get_dir(), 'the_entertainer.wav'))
- self.btest('openal_buffers.c', '0', args=['--preload-file', 'the_entertainer.wav'],)
-
def test_glfw(self):
self.btest('glfw.c', '1', args=['-s', 'LEGACY_GL_EMULATION=1'])
@@ -1262,19 +1193,6 @@ keydown(100);keyup(100); // trigger the end
Popen([PYTHON, EMCC, '-O2', os.path.join(self.get_dir(), 'test_egl_width_height.c'), '-o', 'page.html']).communicate()
self.run_browser('page.html', 'Should print "(300, 150)" -- the size of the canvas in pixels', '/report_result?1')
- def get_freealut_library(self):
- if WINDOWS and Building.which('cmake'):
- return self.get_library('freealut', os.path.join('hello_world.bc'), configure=['cmake', '.'], configure_args=['-DBUILD_TESTS=ON'])
- else:
- return self.get_library('freealut', os.path.join('examples', '.libs', 'hello_world.bc'), make_args=['EXEEXT=.bc'])
-
- def test_freealut(self):
- programs = self.get_freealut_library()
- for program in programs:
- assert os.path.exists(program)
- Popen([PYTHON, EMCC, '-O2', program, '-o', 'page.html']).communicate()
- self.run_browser('page.html', 'You should hear "Hello World!"')
-
def test_worker(self):
# Test running in a web worker
open('file.dat', 'w').write('data for worker')
@@ -1752,6 +1670,23 @@ void *getBindBuffer() {
for mem in [0, 1]:
self.btest('pre_run_deps.cpp', expected='10', args=['--pre-js', 'pre.js', '--memory-init-file', str(mem)])
+ def test_mem_init(self):
+ open(os.path.join(self.get_dir(), 'pre.js'), 'w').write('''
+ function myJSCallback() { // called from main()
+ Module._note(1);
+ }
+ Module.preRun = function() {
+ addOnPreMain(function() {
+ Module._note(2);
+ });
+ };
+ ''')
+ open(os.path.join(self.get_dir(), 'post.js'), 'w').write('''
+ Module._note(4); // this happens too early! and is overwritten when the mem init arrives
+ ''')
+
+ self.btest('mem_init.cpp', expected='3', args=['--pre-js', 'pre.js', '--post-js', 'post.js', '--memory-init-file', '1'])
+
def test_worker_api(self):
Popen([PYTHON, EMCC, path_from_root('tests', 'worker_api_worker.cpp'), '-o', 'worker.js', '-s', 'BUILD_AS_WORKER=1', '-s', 'EXPORTED_FUNCTIONS=["_one"]']).communicate()
self.btest('worker_api_main.cpp', expected='566')
@@ -1851,10 +1786,14 @@ Module["preRun"].push(function () {
self.btest('doublestart.c', args=['--pre-js', 'pre.js', '-o', 'test.html'], expected='1')
def test_html5(self):
- self.btest(path_from_root('tests', 'test_html5.c'), expected='0')
-
- def test_html5_fullscreen(self):
- self.btest(path_from_root('tests', 'test_html5_fullscreen.c'), expected='0')
+ for opts in [[], ['-O2', '-g1', '--closure', '1']]:
+ print opts
+ self.btest(path_from_root('tests', 'test_html5.c'), args=opts, expected='0')
+
+ def test_html5_mouse(self):
+ for opts in [[], ['-O2', '-g1', '--closure', '1']]:
+ print opts
+ self.btest(path_from_root('tests', 'test_html5_mouse.c'), args=opts + ['-DAUTOMATE_SUCCESS=1'], expected='0')
def test_codemods(self):
for opt_level in [0, 2]:
diff --git a/tests/test_core.py b/tests/test_core.py
index 5c34d47e..a954e312 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -1938,7 +1938,7 @@ def process(filename):
def test_emscripten_get_now(self):
if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2')
- if self.run_name == 'o2':
+ if self.run_name == 'slow2asm':
self.emcc_args += ['--closure', '1'] # Use closure here for some additional coverage
self.do_run(open(path_from_root('tests', 'emscripten_get_now.cpp')).read(), 'Timer resolution is good.')
diff --git a/tests/test_html5_fullscreen.c b/tests/test_html5_fullscreen.c
index 9a284b09..54b834bd 100644
--- a/tests/test_html5_fullscreen.c
+++ b/tests/test_html5_fullscreen.c
@@ -3,7 +3,6 @@
#include <string.h>
#include <emscripten/html5.h>
-#ifdef REPORT_RESULT
void report_result(int result)
{
if (result == 0) {
@@ -11,9 +10,10 @@ void report_result(int result)
} else {
printf("Test failed!\n");
}
+#ifdef REPORT_RESULT
REPORT_RESULT();
-}
#endif
+}
static inline const char *emscripten_event_type_to_string(int eventType) {
const char *events[] = { "(invalid)", "(none)", "keypress", "keydown", "keyup", "click", "mousedown", "mouseup", "dblclick", "mousemove", "wheel", "resize",
@@ -90,6 +90,11 @@ EM_BOOL fullscreenchange_callback(int eventType, const EmscriptenFullscreenChang
return 0;
}
+EM_BOOL mouse_callback(int eventType, const EmscriptenMouseEvent *e, void *userData)
+{
+ return 0;
+}
+
int main()
{
EMSCRIPTEN_RESULT ret = emscripten_set_keypress_callback(0, 0, 1, key_callback);
@@ -98,7 +103,21 @@ int main()
ret = emscripten_set_fullscreenchange_callback(0, 0, 1, fullscreenchange_callback);
TEST_RESULT(emscripten_set_fullscreenchange_callback);
+ // For Internet Explorer, fullscreen and pointer lock requests cannot be run
+ // from inside keyboard event handlers. Therefore we must register a callback to
+ // mouse events (any other than mousedown) to activate deferred fullscreen/pointerlock
+ // requests to occur for IE. The callback itself can be a no-op.
+ ret = emscripten_set_click_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_click_callback);
+ ret = emscripten_set_mousedown_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_mousedown_callback);
+ ret = emscripten_set_mouseup_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_mouseup_callback);
+ ret = emscripten_set_dblclick_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_dblclick_callback);
+
printf("To finish this test, press f to enter fullscreen mode, and then exit it.\n");
+ printf("On IE, press a mouse key over the canvas after pressing f to activate the fullscreen request event.\n");
/* For the events to function, one must either call emscripten_set_main_loop or enable Module.noExitRuntime by some other means.
Otherwise the application will exit after leaving main(), and the atexit handlers will clean up all event hooks (by design). */
diff --git a/tests/test_html5_mouse.c b/tests/test_html5_mouse.c
new file mode 100644
index 00000000..f087a62b
--- /dev/null
+++ b/tests/test_html5_mouse.c
@@ -0,0 +1,159 @@
+#include <stdio.h>
+#include <emscripten.h>
+#include <string.h>
+#include <emscripten/html5.h>
+
+void report_result(int result)
+{
+ if (result == 0) {
+ printf("Test successful!\n");
+ } else {
+ printf("Test failed!\n");
+ }
+#ifdef REPORT_RESULT
+ REPORT_RESULT();
+#endif
+}
+
+static inline const char *emscripten_event_type_to_string(int eventType) {
+ const char *events[] = { "(invalid)", "(none)", "keypress", "keydown", "keyup", "click", "mousedown", "mouseup", "dblclick", "mousemove", "wheel", "resize",
+ "scroll", "blur", "focus", "focusin", "focusout", "deviceorientation", "devicemotion", "orientationchange", "fullscreenchange", "pointerlockchange",
+ "visibilitychange", "touchstart", "touchend", "touchmove", "touchcancel", "gamepadconnected", "gamepaddisconnected", "beforeunload",
+ "batterychargingchange", "batterylevelchange", "webglcontextlost", "webglcontextrestored", "(invalid)" };
+ ++eventType;
+ if (eventType < 0) eventType = 0;
+ if (eventType >= sizeof(events)/sizeof(events[0])) eventType = sizeof(events)/sizeof(events[0])-1;
+ return events[eventType];
+}
+
+const char *emscripten_result_to_string(EMSCRIPTEN_RESULT result) {
+ if (result == EMSCRIPTEN_RESULT_SUCCESS) return "EMSCRIPTEN_RESULT_SUCCESS";
+ if (result == EMSCRIPTEN_RESULT_DEFERRED) return "EMSCRIPTEN_RESULT_DEFERRED";
+ if (result == EMSCRIPTEN_RESULT_NOT_SUPPORTED) return "EMSCRIPTEN_RESULT_NOT_SUPPORTED";
+ if (result == EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED) return "EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED";
+ if (result == EMSCRIPTEN_RESULT_INVALID_TARGET) return "EMSCRIPTEN_RESULT_INVALID_TARGET";
+ if (result == EMSCRIPTEN_RESULT_UNKNOWN_TARGET) return "EMSCRIPTEN_RESULT_UNKNOWN_TARGET";
+ if (result == EMSCRIPTEN_RESULT_INVALID_PARAM) return "EMSCRIPTEN_RESULT_INVALID_PARAM";
+ if (result == EMSCRIPTEN_RESULT_FAILED) return "EMSCRIPTEN_RESULT_FAILED";
+ if (result == EMSCRIPTEN_RESULT_NO_DATA) return "EMSCRIPTEN_RESULT_NO_DATA";
+ return "Unknown EMSCRIPTEN_RESULT!";
+}
+
+#define TEST_RESULT(x) if (ret != EMSCRIPTEN_RESULT_SUCCESS) printf("%s returned %s.\n", #x, emscripten_result_to_string(ret));
+
+int gotClick = 0;
+int gotMouseDown = 0;
+int gotMouseUp = 0;
+int gotDblClick = 0;
+int gotMouseMove = 0;
+int gotWheel = 0;
+
+void instruction()
+{
+ if (!gotClick) { printf("Please click on the canvas.\n"); return; }
+ if (!gotMouseDown) { printf("Please click on the canvas.\n"); return; }
+ if (!gotMouseUp) { printf("Please click on the canvas.\n"); return; }
+ if (!gotDblClick) { printf("Please double-click on the canvas.\n"); return; }
+ if (!gotMouseMove) { printf("Please move the mouse on the canvas.\n"); return; }
+ if (!gotWheel) { printf("Please scroll the mouse wheel.\n"); return; }
+
+ if (gotClick && gotMouseDown && gotMouseUp && gotDblClick && gotMouseMove && gotWheel) report_result(0);
+}
+
+EM_BOOL mouse_callback(int eventType, const EmscriptenMouseEvent *e, void *userData)
+{
+ printf("%s, screen: (%ld,%ld), client: (%ld,%ld),%s%s%s%s button: %hu, buttons: %hu, movement: (%ld,%ld), canvas: (%ld,%ld)\n",
+ emscripten_event_type_to_string(eventType), e->screenX, e->screenY, e->clientX, e->clientY,
+ e->ctrlKey ? " CTRL" : "", e->shiftKey ? " SHIFT" : "", e->altKey ? " ALT" : "", e->metaKey ? " META" : "",
+ e->button, e->buttons, e->movementX, e->movementY, e->canvasX, e->canvasY);
+
+ if (e->screenX != 0 && e->screenY != 0 && e->clientX != 0 && e->clientY != 0 && e->canvasX != 0 && e->canvasY != 0)
+ {
+ if (e->buttons != 0)
+ {
+ if (eventType == EMSCRIPTEN_EVENT_CLICK) gotClick = 1;
+ if (eventType == EMSCRIPTEN_EVENT_MOUSEDOWN) gotMouseDown = 1;
+ if (eventType == EMSCRIPTEN_EVENT_DBLCLICK) gotDblClick = 1;
+ }
+ if (eventType == EMSCRIPTEN_EVENT_MOUSEUP) gotMouseUp = 1;
+ if (eventType == EMSCRIPTEN_EVENT_MOUSEMOVE && (e->movementX != 0 || e->movementY != 0)) gotMouseMove = 1;
+ }
+
+ if (eventType == EMSCRIPTEN_EVENT_CLICK && e->screenX == -500000)
+ {
+ printf("ERROR! Received an event to a callback that should have been unregistered!\n");
+ gotClick = 0;
+ report_result(1);
+ }
+
+ instruction();
+ return 0;
+}
+
+EM_BOOL wheel_callback(int eventType, const EmscriptenWheelEvent *e, void *userData)
+{
+ printf("%s, screen: (%ld,%ld), client: (%ld,%ld),%s%s%s%s button: %hu, buttons: %hu, canvas: (%ld,%ld), delta:(%g,%g,%g), deltaMode:%lu\n",
+ emscripten_event_type_to_string(eventType), e->mouse.screenX, e->mouse.screenY, e->mouse.clientX, e->mouse.clientY,
+ e->mouse.ctrlKey ? " CTRL" : "", e->mouse.shiftKey ? " SHIFT" : "", e->mouse.altKey ? " ALT" : "", e->mouse.metaKey ? " META" : "",
+ e->mouse.button, e->mouse.buttons, e->mouse.canvasX, e->mouse.canvasY,
+ (float)e->deltaX, (float)e->deltaY, (float)e->deltaZ, e->deltaMode);
+
+ if (e->deltaY > 0.f || e->deltaY < 0.f)
+ gotWheel = 1;
+
+ instruction();
+ return 0;
+}
+
+int main()
+{
+ EMSCRIPTEN_RESULT ret = emscripten_set_click_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_click_callback);
+ ret = emscripten_set_mousedown_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_mousedown_callback);
+ ret = emscripten_set_mouseup_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_mouseup_callback);
+ ret = emscripten_set_dblclick_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_dblclick_callback);
+ ret = emscripten_set_mousemove_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_mousemove_callback);
+
+ ret = emscripten_set_wheel_callback(0, 0, 1, wheel_callback);
+ TEST_RESULT(emscripten_set_wheel_callback);
+
+#ifdef AUTOMATE_SUCCESS
+ EM_ASM(
+ function sendEvent(type, data) {
+ var event = document.createEvent('Event');
+ event.initEvent(type, true, true);
+ for(var d in data) event[d] = data[d];
+ window.dispatchEvent(event);
+ }
+ sendEvent('click', { screenX: 1, screenY: 1, clientX: 1, clientY: 1, button: 0, buttons: 1 });
+ );
+ // Test that unregistering a callback works. Clicks should no longer be received.
+ ret = emscripten_set_click_callback(0, 0, 1, 0);
+ TEST_RESULT(emscripten_set_click_callback);
+
+ EM_ASM(
+ function sendEvent(type, data) {
+ var event = document.createEvent('Event');
+ event.initEvent(type, true, true);
+ for(var d in data) event[d] = data[d];
+ window.dispatchEvent(event);
+ }
+ sendEvent('click', { screenX: -500000, screenY: -500000, clientX: -500000, clientY: -500000, button: 0, buttons: 1 }); // Send a dummy event that should not be received.
+ sendEvent('mousedown', { screenX: 1, screenY: 1, clientX: 1, clientY: 1, button: 0, buttons: 1 });
+ sendEvent('mouseup', { screenX: 1, screenY: 1, clientX: 1, clientY: 1, button: 0, buttons: 0 });
+ sendEvent('dblclick', { screenX: 1, screenY: 1, clientX: 1, clientY: 1, button: 0, buttons: 1 });
+ sendEvent('mousemove', { screenX: 1, screenY: 1, clientX: 1, clientY: 1, button: 0, buttons: 0, movementX: 1, movementY: 1 });
+ sendEvent('wheel', { screenX: 1, screenY: 1, clientX: 1, clientY: 1, button: 0, buttons: 0, deltaX: 1, deltaY: 1, deltaZ: 1, deltaMode: 1 });
+ sendEvent('mousewheel', { screenX: 1, screenY: 1, clientX: 1, clientY: 1, button: 0, buttons: 0, wheelDeltaX: 1, wheelDeltaY: 1 });
+ );
+#endif
+
+ /* For the events to function, one must either call emscripten_set_main_loop or enable Module.noExitRuntime by some other means.
+ Otherwise the application will exit after leaving main(), and the atexit handlers will clean up all event hooks (by design). */
+ EM_ASM(Module['noExitRuntime'] = true);
+ return 0;
+}
diff --git a/tests/test_interactive.py b/tests/test_interactive.py
new file mode 100644
index 00000000..4ac52f55
--- /dev/null
+++ b/tests/test_interactive.py
@@ -0,0 +1,94 @@
+import BaseHTTPServer, multiprocessing, os, shutil, subprocess, unittest, zlib, webbrowser, time, shlex
+from runner import BrowserCore, path_from_root, nonfastcomp
+from tools.shared import *
+
+# User can specify an environment variable EMSCRIPTEN_BROWSER to force the browser test suite to
+# run using another browser command line than the default system browser.
+emscripten_browser = os.environ.get('EMSCRIPTEN_BROWSER')
+if emscripten_browser:
+ cmd = shlex.split(emscripten_browser)
+ def run_in_other_browser(url):
+ Popen(cmd + [url])
+ webbrowser.open_new = run_in_other_browser
+
+class interactive(BrowserCore):
+ @classmethod
+ def setUpClass(self):
+ super(interactive, self).setUpClass()
+ print
+ print 'Running the browser tests. Make sure the browser allows popups from localhost.'
+ print
+
+ def test_html5_fullscreen(self):
+ self.btest(path_from_root('tests', 'test_html5_fullscreen.c'), expected='0')
+
+ def test_html5_mouse(self):
+ self.btest(path_from_root('tests', 'test_html5_mouse.c'), expected='0')
+
+ def test_sdl_wm_togglefullscreen(self):
+ self.btest('sdl_wm_togglefullscreen.c', expected='1', args=['-s', 'NO_EXIT_RUNTIME=1'])
+
+ def test_sdl_audio(self):
+ shutil.copyfile(path_from_root('tests', 'sounds', 'alarmvictory_1.ogg'), os.path.join(self.get_dir(), 'sound.ogg'))
+ shutil.copyfile(path_from_root('tests', 'sounds', 'alarmcreatemiltaryfoot_1.wav'), os.path.join(self.get_dir(), 'sound2.wav'))
+ shutil.copyfile(path_from_root('tests', 'sounds', 'noise.ogg'), os.path.join(self.get_dir(), 'noise.ogg'))
+ shutil.copyfile(path_from_root('tests', 'sounds', 'the_entertainer.ogg'), os.path.join(self.get_dir(), 'the_entertainer.ogg'))
+ open(os.path.join(self.get_dir(), 'bad.ogg'), 'w').write('I claim to be audio, but am lying')
+ open(os.path.join(self.get_dir(), 'sdl_audio.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio.c')).read()))
+
+ # use closure to check for a possible bug with closure minifying away newer Audio() attributes
+ Popen([PYTHON, EMCC, '-O2', '--closure', '1', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio.c'), '--preload-file', 'sound.ogg', '--preload-file', 'sound2.wav', '--embed-file', 'the_entertainer.ogg', '--preload-file', 'noise.ogg', '--preload-file', 'bad.ogg', '-o', 'page.html', '-s', 'EXPORTED_FUNCTIONS=["_main", "_play", "_play2"]']).communicate()
+ self.run_browser('page.html', '', '/report_result?1')
+
+ def test_sdl_audio_mix_channels(self):
+ shutil.copyfile(path_from_root('tests', 'sounds', 'noise.ogg'), os.path.join(self.get_dir(), 'sound.ogg'))
+ open(os.path.join(self.get_dir(), 'sdl_audio_mix_channels.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio_mix_channels.c')).read()))
+
+ Popen([PYTHON, EMCC, '-O2', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio_mix_channels.c'), '--preload-file', 'sound.ogg', '-o', 'page.html']).communicate()
+ self.run_browser('page.html', '', '/report_result?1')
+
+ def test_sdl_audio_mix(self):
+ shutil.copyfile(path_from_root('tests', 'sounds', 'pluck.ogg'), os.path.join(self.get_dir(), 'sound.ogg'))
+ shutil.copyfile(path_from_root('tests', 'sounds', 'the_entertainer.ogg'), os.path.join(self.get_dir(), 'music.ogg'))
+ shutil.copyfile(path_from_root('tests', 'sounds', 'noise.ogg'), os.path.join(self.get_dir(), 'noise.ogg'))
+ open(os.path.join(self.get_dir(), 'sdl_audio_mix.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio_mix.c')).read()))
+
+ Popen([PYTHON, EMCC, '-O2', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio_mix.c'), '--preload-file', 'sound.ogg', '--preload-file', 'music.ogg', '--preload-file', 'noise.ogg', '-o', 'page.html']).communicate()
+ self.run_browser('page.html', '', '/report_result?1')
+
+ def test_sdl_audio_quickload(self):
+ open(os.path.join(self.get_dir(), 'sdl_audio_quickload.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio_quickload.c')).read()))
+
+ Popen([PYTHON, EMCC, '-O2', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio_quickload.c'), '-o', 'page.html', '-s', 'EXPORTED_FUNCTIONS=["_main", "_play"]']).communicate()
+ self.run_browser('page.html', '', '/report_result?1')
+
+ def test_sdl_audio_beeps(self):
+ open(os.path.join(self.get_dir(), 'sdl_audio_beep.cpp'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio_beep.cpp')).read()))
+
+ # use closure to check for a possible bug with closure minifying away newer Audio() attributes
+ Popen([PYTHON, EMCC, '-O2', '--closure', '1', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio_beep.cpp'), '-s', 'DISABLE_EXCEPTION_CATCHING=0', '-o', 'page.html']).communicate()
+ self.run_browser('page.html', '', '/report_result?1')
+
+ def test_openal_playback(self):
+ shutil.copyfile(path_from_root('tests', 'sounds', 'audio.wav'), os.path.join(self.get_dir(), 'audio.wav'))
+ open(os.path.join(self.get_dir(), 'openal_playback.cpp'), 'w').write(self.with_report_result(open(path_from_root('tests', 'openal_playback.cpp')).read()))
+
+ Popen([PYTHON, EMCC, '-O2', os.path.join(self.get_dir(), 'openal_playback.cpp'), '--preload-file', 'audio.wav', '-o', 'page.html']).communicate()
+ self.run_browser('page.html', '', '/report_result?1')
+
+ def test_openal_buffers(self):
+ shutil.copyfile(path_from_root('tests', 'sounds', 'the_entertainer.wav'), os.path.join(self.get_dir(), 'the_entertainer.wav'))
+ self.btest('openal_buffers.c', '0', args=['--preload-file', 'the_entertainer.wav'],)
+
+ def get_freealut_library(self):
+ if WINDOWS and Building.which('cmake'):
+ return self.get_library('freealut', os.path.join('hello_world.bc'), configure=['cmake', '.'], configure_args=['-DBUILD_TESTS=ON'])
+ else:
+ return self.get_library('freealut', os.path.join('examples', '.libs', 'hello_world.bc'), make_args=['EXEEXT=.bc'])
+
+ def test_freealut(self):
+ programs = self.get_freealut_library()
+ for program in programs:
+ assert os.path.exists(program)
+ Popen([PYTHON, EMCC, '-O2', program, '-o', 'page.html']).communicate()
+ self.run_browser('page.html', 'You should hear "Hello World!"')
diff --git a/tests/test_other.py b/tests/test_other.py
index 9e9c9bef..ded45112 100644
--- a/tests/test_other.py
+++ b/tests/test_other.py
@@ -114,8 +114,6 @@ Options that are modified or new in %s include:
os.chdir(self.get_dir())
self.clear()
- # dlmalloc. dlmalloc is special in that it is the only part of libc that is (1) hard to write well, and
- # very speed-sensitive. So we do not implement it in JS in library.js, instead we compile it from source
for source, has_malloc in [('hello_world' + suffix, False), ('hello_malloc.cpp', True)]:
print source, has_malloc
self.clear()
@@ -193,6 +191,7 @@ Options that are modified or new in %s include:
(['-O2', '-g1'], lambda generated: 'var b = 0' in generated and not 'function _main' in generated, 'compress is cancelled by -g1'),
(['-O2', '-g2'], lambda generated: ('var b = 0' in generated or 'var i1 = 0' in generated) and 'function _main' in generated, 'minify is cancelled by -g2'),
(['-O2', '-g3'], lambda generated: 'var b=0' not in generated and 'var b = 0' not in generated and 'function _main' in generated, 'registerize is cancelled by -g3'),
+ (['-O2', '-profiling'], lambda generated: ('var b = 0' in generated or 'var i1 = 0' in generated) and 'function _main' in generated, 'similar to -g2'),
#(['-O2', '-g4'], lambda generated: 'var b=0' not in generated and 'var b = 0' not in generated and 'function _main' in generated, 'same as -g3 for now'),
(['-s', 'INLINING_LIMIT=0'], lambda generated: 'function _dump' in generated, 'no inlining without opts'),
(['-s', 'USE_TYPED_ARRAYS=0'], lambda generated: 'new Int32Array' not in generated, 'disable typed arrays'),
@@ -1774,6 +1773,8 @@ This pointer might make sense in another type signature: i: 0
['simplifyExpressions', 'optimizeShiftsConservative']),
(path_from_root('tools', 'test-js-optimizer-t2.js'), open(path_from_root('tools', 'test-js-optimizer-t2-output.js')).read(),
['simplifyExpressions', 'optimizeShiftsAggressive']),
+ (path_from_root('tools', 'test-js-optimizer-si.js'), open(path_from_root('tools', 'test-js-optimizer-si-output.js')).read(),
+ ['simplifyIfs']),
# Make sure that optimizeShifts handles functions with shift statements.
(path_from_root('tools', 'test-js-optimizer-t3.js'), open(path_from_root('tools', 'test-js-optimizer-t3-output.js')).read(),
['optimizeShiftsAggressive']),
@@ -2106,6 +2107,7 @@ done.
EM_ASM(Module.print(demangle('__Z5multiwahtjmxyz')));
EM_ASM(Module.print(demangle('__Z1aA32_iPA5_c')));
EM_ASM(Module.print(demangle('__ZN21FWakaGLXFleeflsMarfooC2EjjjPKvbjj')));
+ EM_ASM(Module.print(demangle('__ZN5wakaw2Cm10RasterBaseINS_6watwat9PolocatorEE8merbine1INS4_2OREEEvPKjj'))); // we get this wrong, but at least emit a '?'
one(17);
return 0;
}
@@ -2129,6 +2131,7 @@ parseword(char*&, int, int&)
multi(wchar_t, signed char, unsigned char, unsigned short, unsigned int, unsigned long, long long, unsigned long long, ...)
a(int [32], char [5]*)
FWakaGLXFleeflsMarfoo::FWakaGLXFleeflsMarfoo(unsigned int, unsigned int, unsigned int, void*, bool, unsigned int, unsigned int)
+void wakaw::Cm::RasterBase<wakaw::watwat::Polocator?>(unsigned int*, unsigned int)
''', output)
# test for multiple functions in one stack trace
assert 'one(int)' in output
@@ -2563,3 +2566,102 @@ int main()
p.communicate()
assert p.returncode == 0, 'LLVM tests must pass with exit code 0'
+ def test_odin_validation(self):
+ Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-O1'], stdout=PIPE, stderr=PIPE).communicate()
+ output = run_js('a.out.js', stderr=PIPE, full_output=True, engine=SPIDERMONKEY_ENGINE)
+ assert 'asm.js' in output, 'spidermonkey should mention asm.js compilation: ' + output
+
+ def test_bad_triple(self):
+ Popen([CLANG, path_from_root('tests', 'hello_world.c'), '-c', '-emit-llvm', '-o', 'a.bc'], stdout=PIPE, stderr=PIPE).communicate()
+ out, err = Popen([PYTHON, EMCC, 'a.bc'], stdout=PIPE, stderr=PIPE).communicate()
+ assert 'warning' in err, err
+ assert 'incorrect target triple' in err, err
+
+ def test_simplify_ifs(self):
+ def test(src, nums):
+ open('src.c', 'w').write(src)
+ for opts, ifs in [
+ [['-g2'], nums[0]],
+ [['-profiling'], nums[1]],
+ [['-profiling', '-g2'], nums[2]]
+ ]:
+ print opts, ifs
+ try_delete('a.out.js')
+ Popen([PYTHON, EMCC, 'src.c', '-O2'] + opts, stdout=PIPE).communicate()
+ src = open('a.out.js').read()
+ main = src[src.find('function _main'):src.find('\n}', src.find('function _main'))]
+ actual_ifs = main.count('if (')
+ assert ifs == actual_ifs, main + ' : ' + str([ifs, actual_ifs])
+ #print main
+
+ test(r'''
+ #include <stdio.h>
+ #include <string.h>
+ int main(int argc, char **argv) {
+ if (argc > 5 && strlen(argv[0]) > 1 && strlen(argv[1]) > 2) printf("halp");
+ return 0;
+ }
+ ''', [3, 1, 1])
+
+ test(r'''
+ #include <stdio.h>
+ #include <string.h>
+ int main(int argc, char **argv) {
+ while (argc % 3 == 0) {
+ if (argc > 5 && strlen(argv[0]) > 1 && strlen(argv[1]) > 2) {
+ printf("halp");
+ argc++;
+ } else {
+ while (argc > 0) {
+ printf("%d\n", argc--);
+ }
+ }
+ }
+ return 0;
+ }
+ ''', [8, 5, 5])
+
+ test(r'''
+ #include <stdio.h>
+ #include <string.h>
+ int main(int argc, char **argv) {
+ while (argc % 17 == 0) argc *= 2;
+ if (argc > 5 && strlen(argv[0]) > 10 && strlen(argv[1]) > 20) {
+ printf("halp");
+ argc++;
+ } else {
+ printf("%d\n", argc--);
+ }
+ while (argc % 17 == 0) argc *= 2;
+ return argc;
+ }
+ ''', [6, 3, 3])
+
+ test(r'''
+ #include <stdio.h>
+ #include <stdlib.h>
+
+ int main(int argc, char *argv[]) {
+ if (getenv("A") && getenv("B")) {
+ printf("hello world\n");
+ } else {
+ printf("goodnight moon\n");
+ }
+ printf("and that's that\n");
+ return 0;
+ }
+ ''', [3, 1, 1])
+
+ test(r'''
+ #include <stdio.h>
+ #include <stdlib.h>
+
+ int main(int argc, char *argv[]) {
+ if (getenv("A") || getenv("B")) {
+ printf("hello world\n");
+ }
+ printf("and that's that\n");
+ return 0;
+ }
+ ''', [3, 1, 1])
+
diff --git a/tests/test_sockets.py b/tests/test_sockets.py
index 3a5555ed..d8133b0b 100644
--- a/tests/test_sockets.py
+++ b/tests/test_sockets.py
@@ -345,12 +345,12 @@ class sockets(BrowserCore):
host_outfile = 'host.html'
peer_outfile = 'peer.html'
- host_filepath = path_from_root('tests', 'sockets', host_src)
+ host_filepath = path_from_root('tests', 'sockets', host_src)
temp_host_filepath = os.path.join(self.get_dir(), os.path.basename(host_src))
with open(host_filepath) as f: host_src = f.read()
with open(temp_host_filepath, 'w') as f: f.write(self.with_report_result(host_src))
- peer_filepath = path_from_root('tests', 'sockets', peer_src)
+ peer_filepath = path_from_root('tests', 'sockets', peer_src)
temp_peer_filepath = os.path.join(self.get_dir(), os.path.basename(peer_src))
with open(peer_filepath) as f: peer_src = f.read()
with open(temp_peer_filepath, 'w') as f: f.write(self.with_report_result(peer_src))
@@ -358,7 +358,7 @@ class sockets(BrowserCore):
open(os.path.join(self.get_dir(), 'host_pre.js'), 'w').write('''
var Module = {
webrtc: {
- broker: 'https://mdsw.ch:8080',
+ broker: 'http://localhost:8080',
session: undefined,
onpeer: function(peer, route) {
window.open('http://localhost:8888/peer.html?' + route);
@@ -382,7 +382,7 @@ class sockets(BrowserCore):
open(os.path.join(self.get_dir(), 'peer_pre.js'), 'w').write('''
var Module = {
webrtc: {
- broker: 'https://mdsw.ch:8080',
+ broker: 'http://localhost:8080',
session: window.location.toString().split('?')[1],
onpeer: function(peer, route) {
peer.connect(Module['webrtc']['session']);
@@ -403,9 +403,14 @@ class sockets(BrowserCore):
Popen([PYTHON, EMCC, temp_host_filepath, '-o', host_outfile] + ['-s', 'GL_TESTING=1', '--pre-js', 'host_pre.js', '-s', 'SOCKET_WEBRTC=1', '-s', 'SOCKET_DEBUG=1']).communicate()
Popen([PYTHON, EMCC, temp_peer_filepath, '-o', peer_outfile] + ['-s', 'GL_TESTING=1', '--pre-js', 'peer_pre.js', '-s', 'SOCKET_WEBRTC=1', '-s', 'SOCKET_DEBUG=1']).communicate()
+ Popen(['npm', 'install', path_from_root('tests', 'sockets', 'p2p')]).communicate();
+ broker = Popen([NODE_JS, path_from_root('tests', 'sockets', 'p2p', 'broker', 'p2p-broker.js')]);
+
expected = '1'
self.run_browser(host_outfile, '.', ['/report_result?' + e for e in expected])
+ broker.kill();
+
def test_nodejs_sockets_echo(self):
# This test checks that sockets work when the client code is run in Node.js
# Run with ./runner.py sockets.test_nodejs_sockets_echo
@@ -414,21 +419,54 @@ class sockets(BrowserCore):
sockets_include = '-I'+path_from_root('tests', 'sockets')
- # Websockify-proxied servers can't run dgram tests
harnesses = [
- # Websockify doesn't seem to like ws.WebSocket clients TODO check if this is a ws issue or Websockify issue
- #(WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include], 49160), 0),
- (CompiledServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include, '-DTEST_DGRAM=0'], 49161), 0),
- (CompiledServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include, '-DTEST_DGRAM=1'], 49162), 1)
+ (WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include], 59160), 0),
+ (CompiledServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include, '-DTEST_DGRAM=0'], 59162), 0),
+ (CompiledServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include, '-DTEST_DGRAM=1'], 59164), 1)
]
+ # Basic test of node client against both a Websockified and compiled echo server.
for harness, datagram in harnesses:
with harness:
- Popen([PYTHON, EMCC, path_from_root('tests', 'sockets', 'test_sockets_echo_client.c'), '-o', path_from_root('tests', 'sockets', 'client.js'), '-DSOCKK=%d' % harness.listen_port, '-DREPORT_RESULT=int dummy'], stdout=PIPE, stderr=PIPE).communicate()
+ Popen([PYTHON, EMCC, path_from_root('tests', 'sockets', 'test_sockets_echo_client.c'), '-o', 'client.js', '-DSOCKK=%d' % harness.listen_port, '-DTEST_DGRAM=%d' % datagram, '-DREPORT_RESULT=int dummy'], stdout=PIPE, stderr=PIPE).communicate()
+
+ out = run_js('client.js', engine=NODE_JS, full_output=True)
+ self.assertContained('do_msg_read: read 14 bytes', out)
+
+ # Test against a Websockified server with compile time configured WebSocket subprotocol. We use a Websockified
+ # server because as long as the subprotocol list contains binary it will configure itself to accept binary
+ # This test also checks that the connect url contains the correct subprotocols.
+ print "\nTesting compile time WebSocket configuration.\n"
+ for harness in [
+ WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include], 59166)
+ ]:
+ with harness:
+ Popen([PYTHON, EMCC, path_from_root('tests', 'sockets', 'test_sockets_echo_client.c'), '-o', 'client.js', '-s', 'SOCKET_DEBUG=1', '-s', 'WEBSOCKET_SUBPROTOCOL="base64, binary"', '-DSOCKK=59166', '-DREPORT_RESULT=int dummy'], stdout=PIPE, stderr=PIPE).communicate()
+
+ out = run_js('client.js', engine=NODE_JS, full_output=True)
+ self.assertContained('do_msg_read: read 14 bytes', out)
+ self.assertContained('connect: ws://127.0.0.1:59166, base64,binary', out)
+
+ # Test against a Websockified server with runtime WebSocket configuration. We specify both url and subprotocol.
+ # In this test we have *deliberately* used the wrong port '-DSOCKK=12345' to configure the echo_client.c, so
+ # the connection would fail without us specifying a valid WebSocket URL in the configuration.
+ print "\nTesting runtime WebSocket configuration.\n"
+ for harness in [
+ WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include], 59168)
+ ]:
+ with harness:
+ open(os.path.join(self.get_dir(), 'websocket_pre.js'), 'w').write('''
+ var Module = {
+ websocket: {
+ url: 'ws://localhost:59168/testA/testB',
+ subprotocol: 'text, base64, binary',
+ }
+ };
+ ''')
- self.assertContained('do_msg_read: read 14 bytes', run_js(path_from_root('tests', 'sockets', 'client.js'), engine=NODE_JS))
+ Popen([PYTHON, EMCC, path_from_root('tests', 'sockets', 'test_sockets_echo_client.c'), '-o', 'client.js', '--pre-js', 'websocket_pre.js', '-s', 'SOCKET_DEBUG=1', '-DSOCKK=12345', '-DREPORT_RESULT=int dummy'], stdout=PIPE, stderr=PIPE).communicate()
- # Tidy up files that might have been created by this test.
- try_delete(path_from_root('tests', 'sockets', 'client.js'))
- try_delete(path_from_root('tests', 'sockets', 'client.js.map'))
+ out = run_js('client.js', engine=NODE_JS, full_output=True)
+ self.assertContained('do_msg_read: read 14 bytes', out)
+ self.assertContained('connect: ws://localhost:59168/testA/testB, text,base64,binary', out)
diff --git a/tools/file_packager.py b/tools/file_packager.py
index 12cc5475..0ab68511 100644
--- a/tools/file_packager.py
+++ b/tools/file_packager.py
@@ -113,7 +113,7 @@ for arg in sys.argv[2:]:
try:
from shared import CRUNCH
except Exception, e:
- print >> sys.stderr, 'count not import CRUNCH (make sure it is defined properly in ~/.emscripten)'
+ print >> sys.stderr, 'could not import CRUNCH (make sure it is defined properly in ~/.emscripten)'
raise e
crunch = arg.split('=')[1] if '=' in arg else '128'
leading = ''
@@ -123,12 +123,13 @@ for arg in sys.argv[2:]:
leading = ''
elif leading == 'preload' or leading == 'embed':
mode = leading
- if '@' in arg:
+ uses_at_notation = '@' in arg
+ if uses_at_notation:
srcpath, dstpath = arg.split('@') # User is specifying destination filename explicitly.
else:
srcpath = dstpath = arg # Use source path as destination path.
if os.path.isfile(srcpath) or os.path.isdir(srcpath):
- data_files.append({ 'srcpath': srcpath, 'dstpath': dstpath, 'mode': mode })
+ data_files.append({ 'srcpath': srcpath, 'dstpath': dstpath, 'mode': mode, 'explicit_dst_path': uses_at_notation })
else:
print >> sys.stderr, 'Warning: ' + arg + ' does not exist, ignoring.'
elif leading == 'exclude':
@@ -206,7 +207,7 @@ def add(arg, dirname, names):
new_names.append(name)
if not os.path.isdir(fullname):
dstpath = os.path.join(rootpathdst, os.path.relpath(fullname, rootpathsrc)) # Convert source filename relative to root directory of target FS.
- new_data_files.append({ 'srcpath': fullname, 'dstpath': dstpath, 'mode': mode })
+ new_data_files.append({ 'srcpath': fullname, 'dstpath': dstpath, 'mode': mode, 'explicit_dst_path': True })
del names[:]
names.extend(new_names)
@@ -223,13 +224,14 @@ if len(data_files) == 0:
sys.exit(1)
# Absolutize paths, and check that they make sense
-curr_abspath = os.path.abspath(os.getcwd())
+curr_abspath = os.path.abspath(os.getcwd()) # os.getcwd() always returns the hard path with any symbolic links resolved, even if we cd'd into a symbolic link.
+
for file_ in data_files:
- if file_['srcpath'] == file_['dstpath']:
+ if not file_['explicit_dst_path']:
# This file was not defined with src@dst, so we inferred the destination from the source. In that case,
# we require that the destination not be under the current location
path = file_['dstpath']
- abspath = os.path.abspath(path)
+ abspath = os.path.realpath(os.path.abspath(path)) # Use os.path.realpath to resolve any symbolic links to hard paths, to match the structure in curr_abspath.
if DEBUG: print >> sys.stderr, path, abspath, curr_abspath
if not abspath.startswith(curr_abspath):
print >> sys.stderr, 'Error: Embedding "%s" which is below the current directory "%s". This is invalid since the current directory becomes the root that the generated code will see' % (path, curr_abspath)
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index 240ee2bd..129c493f 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -140,6 +140,8 @@ var ALTER_FLOW = set('break', 'continue', 'return');
var BREAK_CAPTURERS = set('do', 'while', 'for', 'switch');
var CONTINUE_CAPTURERS = LOOP;
+var COMMABLE = set('assign', 'binary', 'unary-prefix', 'unary-postfix', 'name', 'num', 'call', 'seq', 'conditional', 'sub');
+
var FUNCTIONS_THAT_ALWAYS_THROW = set('abort', '___resumeException', '___cxa_throw', '___cxa_rethrow');
var NULL_NODE = ['name', 'null'];
@@ -279,6 +281,38 @@ function clearEmptyNodes(list) {
}
}
+function filterEmptyNodes(list) { // creates a copy and returns it
+ return list.filter(function(node) {
+ return !(isEmptyNode(node) || (node[0] === 'stat' && isEmptyNode(node[1])));
+ });
+}
+
+function removeEmptySubNodes(node) {
+ if (node[0] === 'defun') {
+ node[3] = filterEmptyNodes(node[3]);
+ } else if (node[0] === 'block' && node[1]) {
+ node[1] = filterEmptyNodes(node[1]);
+ }
+/*
+ var stats = getStatements(node);
+ if (stats) clearEmptyNodes(stats);
+*/
+}
+
+function removeAllEmptySubNodes(ast) {
+ traverse(ast, removeEmptySubNodes);
+}
+
+function astCompare(x, y) {
+ if (!Array.isArray(x)) return x === y;
+ if (!Array.isArray(y)) return false;
+ if (x.length !== y.length) return false;
+ for (var i = 0; i < x.length; i++) {
+ if (!astCompare(x[i], y[i])) return false;
+ }
+ return true;
+}
+
// Passes
// Dump the AST. Useful for debugging. For example,
@@ -414,7 +448,7 @@ function removeUnneededLabelSettings(ast) {
});
}
-// Various expression simplifications. Pre run before closure (where we still have metadata), Post run after.
+// Various expression simplifications. Happens after elimination, which opens up many of these simplification opportunities.
var USEFUL_BINARY_OPS = set('<<', '>>', '|', '&', '^');
var COMPARE_OPS = set('<', '<=', '>', '>=', '==', '===', '!=', '!==');
@@ -797,11 +831,166 @@ function simplifyExpressions(ast) {
simplifyIntegerConversions(func);
simplifyBitops(func);
joinAdditions(func);
- // simplifyZeroComp(func); TODO: investigate performance
simplifyNotComps(func);
+ // simplifyZeroComp(func); TODO: investigate performance
+ });
+}
+
+
+function simplifyIfs(ast) {
+ traverseGeneratedFunctions(ast, function(func) {
+ var simplifiedAnElse = false;
+
+ traverse(func, function(node, type) {
+ // simplify if (x) { if (y) { .. } } to if (x ? y : 0) { .. }
+ if (type === 'if') {
+ var body = node[2];
+ // recurse to handle chains
+ while (body[0] === 'block') {
+ var stats = body[1];
+ if (stats.length === 0) break;
+ var other = stats[stats.length-1];
+ if (other[0] !== 'if') {
+ // our if block does not end with an if. perhaps if have an else we can flip
+ if (node[3] && node[3][0] === 'block') {
+ var stats = node[3][1];
+ if (stats.length === 0) break;
+ var other = stats[stats.length-1];
+ if (other[0] === 'if') {
+ // flip node
+ node[1] = flipCondition(node[1]);
+ node[2] = node[3];
+ node[3] = body;
+ body = node[2];
+ } else break;
+ } else break;
+ }
+ // we can handle elses, but must be fully identical
+ if (node[3] || other[3]) {
+ if (!node[3]) break;
+ if (!astCompare(node[3], other[3])) {
+ // the elses are different, but perhaps if we flipped a condition we can do better
+ if (astCompare(node[3], other[2])) {
+ // flip other. note that other may not have had an else! add one if so; we will eliminate such things later
+ if (!other[3]) other[3] = ['block', []];
+ other[1] = flipCondition(other[1]);
+ var temp = other[2];
+ other[2] = other[3];
+ other[3] = temp;
+ } else break;
+ }
+ }
+ if (stats.length > 1) {
+ // try to commaify - turn everything between the ifs into a comma operator inside the second if
+ var ok = true;
+ for (var i = 0; i < stats.length-1; i++) {
+ var curr = stats[i];
+ if (curr[0] === 'stat') curr = curr[1];
+ if (!(curr[0] in COMMABLE)) ok = false;
+ }
+ if (!ok) break;
+ for (var i = stats.length-2; i >= 0; i--) {
+ var curr = stats[i];
+ if (curr[0] === 'stat') curr = curr[1];
+ other[1] = ['seq', curr, other[1]];
+ }
+ stats = body[1] = [other];
+ }
+ if (stats.length !== 1) break;
+ if (node[3]) simplifiedAnElse = true;
+ node[1] = ['conditional', node[1], other[1], ['num', 0]];
+ body = node[2] = other[2];
+ }
+ }
+ });
+
+ if (simplifiedAnElse) {
+ // there may be fusing opportunities
+
+ // we can only fuse if we remove all uses of the label. if there are
+ // other ones - if the label check can be reached from elsewhere -
+ // we must leave it
+ var abort = false;
+
+ var labelAssigns = {};
+ traverse(func, function(node, type) {
+ if (type === 'assign' && node[2][0] === 'name' && node[2][1] === 'label') {
+ if (node[3][0] === 'num') {
+ var value = node[3][1];
+ labelAssigns[value] = (labelAssigns[value] || 0) + 1;
+ } else {
+ // label is assigned a dynamic value (like from indirectbr), we cannot do anything
+ abort = true;
+ }
+ }
+ });
+ if (abort) return;
+
+ var labelChecks = {};
+ traverse(func, function(node, type) {
+ if (type === 'binary' && node[1] === '==' && node[2][0] === 'binary' && node[2][1] === '|' &&
+ node[2][2][0] === 'name' && node[2][2][1] === 'label') {
+ if (node[3][0] === 'num') {
+ var value = node[3][1];
+ labelChecks[value] = (labelChecks[value] || 0) + 1;
+ } else {
+ // label is checked vs a dynamic value (like from indirectbr), we cannot do anything
+ abort = true;
+ }
+ }
+ });
+ if (abort) return;
+
+ var inLoop = 0; // when in a loop, we do not emit label = 0; in the relooper as there is no need
+ traverse(func, function(node, type) {
+ if (type === 'while') inLoop++;
+ var stats = getStatements(node);
+ if (stats) {
+ for (var i = 0; i < stats.length-1; i++) {
+ var pre = stats[i];
+ var post = stats[i+1];
+ if (pre[0] === 'if' && pre[3] && post[0] === 'if' && !post[3]) {
+ var postCond = post[1];
+ if (postCond[0] === 'binary' && postCond[1] === '==' &&
+ postCond[2][0] === 'binary' && postCond[2][1] === '|' &&
+ postCond[2][2][0] === 'name' && postCond[2][2][1] === 'label' &&
+ postCond[2][3][0] === 'num' && postCond[2][3][1] === 0 &&
+ postCond[3][0] === 'num') {
+ var postValue = postCond[3][1];
+ var preElse = pre[3];
+ if (labelAssigns[postValue] === 1 && labelChecks[postValue] === 1 && preElse[0] === 'block' && preElse[1] && preElse[1].length === 1) {
+ var preStat = preElse[1][0];
+ if (preStat[0] === 'stat' && preStat[1][0] === 'assign' &&
+ preStat[1][1] === true && preStat[1][2][0] === 'name' && preStat[1][2][1] === 'label' &&
+ preStat[1][3][0] === 'num' && preStat[1][3][1] === postValue) {
+ // Conditions match, just need to make sure the post clears label
+ if (post[2][0] === 'block' && post[2][1] && post[2][1].length > 0) {
+ var postStat = post[2][1][0];
+ var haveClear =
+ postStat[0] === 'stat' && postStat[1][0] === 'assign' &&
+ postStat[1][1] === true && postStat[1][2][0] === 'name' && postStat[1][2][1] === 'label' &&
+ postStat[1][3][0] === 'num' && postStat[1][3][1] === 0;
+ if (!inLoop || haveClear) {
+ // Everything lines up, do it
+ pre[3] = post[2];
+ if (haveClear) pre[3][1].splice(0, 1); // remove the label clearing
+ stats.splice(i+1, 1); // remove the post entirely
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }, function(node, type) {
+ if (type === 'while') inLoop--;
+ });
+ }
});
}
+
// In typed arrays mode 2, we can have
// HEAP[x >> 2]
// very often. We can in some cases do the shift on the variable itself when it is set,
@@ -1161,6 +1350,10 @@ function simplifyNotCompsDirect(node) {
if (!simplifyNotCompsPass) return node;
}
+function flipCondition(cond) {
+ return simplifyNotCompsDirect(['unary-prefix', '!', cond]);
+}
+
var simplifyNotCompsPass = false;
function simplifyNotComps(ast) {
@@ -1281,6 +1474,7 @@ function vacuum(ast) {
traverseGeneratedFunctions(ast, function(node) {
vacuumInternal(node);
simplifyNotComps(node);
+ removeEmptySubNodes(node);
});
}
@@ -1426,7 +1620,7 @@ function hoistMultiples(ast) {
var temp = node[3];
node[3] = node[2];
node[2] = temp;
- node[1] = simplifyNotCompsDirect(['unary-prefix', '!', node[1]]);
+ node[1] = flipCondition(node[1]);
stat1 = node[2][1];
stat2 = node[3][1];
}
@@ -1687,6 +1881,11 @@ function denormalizeAsm(func, data) {
if (!isEmptyNode(stats[i])) break;
}
}
+ // calculate variable definitions
+ var varDefs = [];
+ for (var v in data.vars) {
+ varDefs.push(makeAsmVarDef(v, data.vars[v]));
+ }
// each param needs a line; reuse emptyNodes as much as we can
var numParams = 0;
for (var i in data.params) numParams++;
@@ -1695,26 +1894,21 @@ function denormalizeAsm(func, data) {
if (!isEmptyNode(stats[emptyNodes])) break;
emptyNodes++;
}
- var neededEmptyNodes = numParams + 1; // params plus one big var
+ var neededEmptyNodes = numParams + (varDefs.length ? 1 : 0); // params plus one big var if there are vars
if (neededEmptyNodes > emptyNodes) {
var args = [0, 0];
for (var i = 0; i < neededEmptyNodes - emptyNodes; i++) args[i+2] = 0;
stats.splice.apply(stats, args);
+ } else if (neededEmptyNodes < emptyNodes) {
+ stats.splice(0, emptyNodes - neededEmptyNodes);
}
// add param coercions
var next = 0;
func[2].forEach(function(param) {
stats[next++] = ['stat', ['assign', true, ['name', param], makeAsmCoercion(['name', param], data.params[param])]];
});
- // add variable definitions
- var varDefs = [];
- for (var v in data.vars) {
- varDefs.push(makeAsmVarDef(v, data.vars[v]));
- }
if (varDefs.length) {
stats[next] = ['var', varDefs];
- } else {
- stats[next] = emptyNode();
}
if (data.inlines.length > 0) {
var i = 0;
@@ -3877,6 +4071,8 @@ function eliminate(ast, memSafe) {
}
new ExpressionOptimizer(ast).run();
}
+
+ removeAllEmptySubNodes(ast);
}
function eliminateMemSafe(ast) {
@@ -4247,6 +4443,8 @@ function aggressiveVariableEliminationInternal(func, asmData) {
}
}
});
+
+ removeAllEmptySubNodes(func);
}
function aggressiveVariableElimination(ast) {
@@ -5235,6 +5433,7 @@ var passes = {
removeAssignsToUndefined: removeAssignsToUndefined,
//removeUnneededLabelSettings: removeUnneededLabelSettings,
simplifyExpressions: simplifyExpressions,
+ simplifyIfs: simplifyIfs,
optimizeShiftsConservative: optimizeShiftsConservative,
optimizeShiftsAggressive: optimizeShiftsAggressive,
hoistMultiples: hoistMultiples,
@@ -5281,7 +5480,15 @@ if (extraInfoStart > 0) extraInfo = JSON.parse(src.substr(extraInfoStart + 14));
arguments_.slice(1).forEach(function(arg) {
+ //traverse(ast, function(node) {
+ // if (node[0] === 'defun' && node[1] === 'copyTempFloat') printErr('pre ' + JSON.stringify(node, null, ' '));
+ //});
passes[arg](ast);
+ //var func;
+ //traverse(ast, function(node) {
+ // if (node[0] === 'defun') func = node;
+ // if (isEmptyNode(node)) throw 'empty node after ' + arg + ', in ' + func[1];
+ //});
});
if (asm && last) {
asmLastOpts(ast); // TODO: move out of last, to make last faster when done later (as in side modules)
diff --git a/tools/profile_stripper.py b/tools/profile_stripper.py
new file mode 100644
index 00000000..3e538ef3
--- /dev/null
+++ b/tools/profile_stripper.py
@@ -0,0 +1,43 @@
+# See profile_used.py
+#
+# profile file, js file
+
+import sys, json
+
+used = json.loads(open(sys.argv[1]).read())
+
+show = True
+in_table = False
+
+for orig in open(sys.argv[2]).readlines():
+ line = orig.strip()
+
+ if orig.startswith('function _') and line.endswith(('){', ') {')):
+ name = line.split(' ')[1].split('(')[0]
+ if name.startswith('_') and not used.get(name):
+ #print >> sys.stderr, 'remove', name
+ show = False
+
+ if line.startswith('var FUNCTION_TABLE'):
+ in_table = True
+
+ if in_table:
+ start = 0
+ if 'var ' in line:
+ start = line.index('[')+1
+ end = len(line)
+ if ']' in line:
+ end = line.index(']')
+ contents = line[start:end]
+ fixed = map(lambda name: '"' + name + '"' if not used.get(name) else name, contents.split(','))
+ print (line[:start] + ','.join(fixed) + line[end:]).replace('""', '')
+ else:
+ if show:
+ print orig,
+
+ if orig.startswith('}'):
+ show = True
+
+ if in_table and line.endswith(';'):
+ in_table = False
+
diff --git a/tools/profile_used.py b/tools/profile_used.py
new file mode 100644
index 00000000..45420e0f
--- /dev/null
+++ b/tools/profile_used.py
@@ -0,0 +1,17 @@
+# Run, then execute
+'''
+dump(JSON.stringify(usedFunctions))
+'''
+# then strip with profile_strip.py
+
+import sys
+
+print 'var usedFunctions = {};'
+
+for line in open(sys.argv[1]).readlines():
+ line = line.strip()
+ print line
+ if line.startswith('function _') and line.endswith(('){', ') {')):
+ name = line.split(' ')[1].split('(')[0]
+ print 'usedFunctions["%s"] = 1;' % name
+
diff --git a/tools/shared.py b/tools/shared.py
index 533906c9..1adbdfaa 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -316,8 +316,10 @@ def check_fastcomp():
llvm_version = open(os.path.join(d, 'emscripten-version.txt')).read().strip()
if os.path.exists(os.path.join(d, 'tools', 'clang', 'emscripten-version.txt')):
clang_version = open(os.path.join(d, 'tools', 'clang', 'emscripten-version.txt')).read().strip()
+ elif os.path.exists(os.path.join(d, 'tools', 'clang')):
+ clang_version = '?' # Looks like the LLVM compiler tree has an old checkout from the time before it contained a version.txt: Should update!
else:
- clang_version = '?'
+ clang_version = llvm_version # This LLVM compiler tree does not have a tools/clang, so it's probably an out-of-source build directory. No need for separate versioning.
if EMSCRIPTEN_VERSION != llvm_version or EMSCRIPTEN_VERSION != clang_version:
logging.error('Emscripten, llvm and clang versions do not match, this is dangerous (%s, %s, %s)', EMSCRIPTEN_VERSION, llvm_version, clang_version)
logging.error('Make sure to use the same branch in each repo, and to be up-to-date on each. See https://github.com/kripken/emscripten/wiki/LLVM-Backend')
@@ -707,13 +709,14 @@ else:
# Engine tweaks
try:
+ SPIDERMONKEY_ENGINE = listify(SPIDERMONKEY_ENGINE)
if 'gcparam' not in str(SPIDERMONKEY_ENGINE):
new_spidermonkey = SPIDERMONKEY_ENGINE
- if type(SPIDERMONKEY_ENGINE) is str:
- new_spidermonkey = [SPIDERMONKEY_ENGINE]
new_spidermonkey += ['-e', "gcparam('maxBytes', 1024*1024*1024);"] # Our very large files need lots of gc heap
JS_ENGINES = map(lambda x: x if x != SPIDERMONKEY_ENGINE else new_spidermonkey, JS_ENGINES)
SPIDERMONKEY_ENGINE = new_spidermonkey
+ if '-w' not in SPIDERMONKEY_ENGINE:
+ SPIDERMONKEY_ENGINE += ['-w']
except NameError:
pass
diff --git a/tools/system_libs.py b/tools/system_libs.py
index 3723a5c3..ee185a51 100644
--- a/tools/system_libs.py
+++ b/tools/system_libs.py
@@ -335,8 +335,10 @@ def calculate(temp_files, in_temp, stdout, stderr):
# Settings this in the environment will avoid checking dependencies and make building big projects a little faster
# 1 means include everything; otherwise it can be the name of a lib (libcxx, etc.)
+ # You can provide 1 to include everything, or a comma-separated list with the ones you want
force = os.environ.get('EMCC_FORCE_STDLIBS')
force_all = force == '1'
+ force = set((force or '').split(','))
# Scan symbols
symbolses = map(lambda temp_file: shared.Building.llvm_nm(temp_file), temp_files)
@@ -364,15 +366,14 @@ def calculate(temp_files, in_temp, stdout, stderr):
all_needed.difference_update(symbols.defs)
# Go over libraries to figure out which we must include
- # If we have libcxx, we must force inclusion of libc, since libcxx uses new internally. Note: this is kind of hacky.
ret = []
has = need = None
- for name, create, apply_, library_symbols in [('libcxx', create_libcxx, apply_libcxx, libcxx_symbols),
- ('libcextra', create_libcextra, lambda x: True, libcextra_symbols),
- ('libcxxabi', create_libcxxabi, apply_libcxxabi, libcxxabi_symbols),
- ('gl', create_gl, lambda x: True, gl_symbols),
- ('libc', create_libc, apply_libc, libc_symbols)]:
- force_this = force_all or force == name
+ for name, create, apply_, library_symbols, deps in [('libcxx', create_libcxx, apply_libcxx, libcxx_symbols, ['libcextra', 'libcxxabi']),
+ ('libcextra', create_libcextra, lambda x: True, libcextra_symbols, ['libc']),
+ ('libcxxabi', create_libcxxabi, apply_libcxxabi, libcxxabi_symbols, ['libc']),
+ ('gl', create_gl, lambda x: True, gl_symbols, ['libc']),
+ ('libc', create_libc, apply_libc, libc_symbols, [])]:
+ force_this = force_all or name in force
if not force_this:
need = set()
has = set()
@@ -387,11 +388,11 @@ def calculate(temp_files, in_temp, stdout, stderr):
need.remove(haz)
if shared.Settings.VERBOSE: logging.debug('considering %s: we need %s and have %s' % (name, str(need), str(has)))
if force_this or len(need) > 0:
- force_all = True
if apply_(need):
# We need to build and link the library in
logging.debug('including %s' % name)
libfile = shared.Cache.get(name, create)
ret.append(libfile)
+ force = force.union(deps)
return ret
diff --git a/tools/test-js-optimizer-asm-outline1-output.js b/tools/test-js-optimizer-asm-outline1-output.js
index c4792c51..40c028c6 100644
--- a/tools/test-js-optimizer-asm-outline1-output.js
+++ b/tools/test-js-optimizer-asm-outline1-output.js
@@ -176,11 +176,12 @@ function vars(x, y) {
y = +y;
var sp = 0;
sp = STACKTOP;
- STACKTOP = STACKTOP + 152 | 0;
+ STACKTOP = STACKTOP + 136 | 0;
c(1 + (x + y));
c(2 + y * x);
c(3 + (x + y));
c(4 + y * x);
+ c(5 + (x + y));
HEAP32[sp + 8 >> 2] = x;
HEAPF32[sp + 16 >> 2] = y;
HEAP32[sp + 24 >> 2] = 0;
@@ -213,7 +214,7 @@ function vars3(x, y) {
y = +y;
var a = 0, sp = 0;
sp = STACKTOP;
- STACKTOP = STACKTOP + 160 | 0;
+ STACKTOP = STACKTOP + 144 | 0;
a = x + y;
a = c(1 + a);
a = c(2 + y * x);
@@ -256,15 +257,11 @@ function vars_w_stack(x, y) {
var a = 0, b = +0, sp = 0;
sp = STACKTOP;
STACKTOP = STACKTOP + 208 | 0;
- HEAP32[sp + 24 >> 2] = x;
- HEAPF32[sp + 32 >> 2] = y;
- HEAP32[sp + 40 >> 2] = a;
- HEAPF32[sp + 48 >> 2] = b;
- HEAP32[sp + 72 >> 2] = 0;
- HEAP32[sp + 76 >> 2] = 0;
- vars_w_stack$1(sp);
- a = HEAP32[sp + 40 >> 2] | 0;
- b = +HEAPF32[sp + 48 >> 2];
+ a = x + y;
+ b = y * x;
+ a = c(1 + a);
+ a = c(2 + a);
+ a = c(3 + a);
HEAP32[sp + 40 >> 2] = a;
HEAPF32[sp + 48 >> 2] = b;
HEAP32[sp + 64 >> 2] = 0;
@@ -585,10 +582,9 @@ function mix$1(sp) {
}
function vars$0(sp) {
sp = sp | 0;
- var x = 0, y = +0;
+ var y = +0, x = 0;
x = HEAP32[sp + 8 >> 2] | 0;
y = +HEAPF32[sp + 16 >> 2];
- c(5 + (x + y));
c(6 + y * x);
c(7 + (x + y));
c(8 + y * x);
@@ -632,6 +628,7 @@ function vars_w_stack$0(sp) {
var a = 0, b = +0;
a = HEAP32[sp + 40 >> 2] | 0;
b = +HEAPF32[sp + 48 >> 2];
+ a = c(4 + a);
a = c(5 + a);
a = c(6 + a);
b = c(7 + a);
@@ -639,22 +636,6 @@ function vars_w_stack$0(sp) {
HEAP32[sp + 40 >> 2] = a;
HEAPF32[sp + 48 >> 2] = b;
}
-function vars_w_stack$1(sp) {
- sp = sp | 0;
- var a = 0, x = 0, y = +0, b = +0;
- x = HEAP32[sp + 24 >> 2] | 0;
- y = +HEAPF32[sp + 32 >> 2];
- a = HEAP32[sp + 40 >> 2] | 0;
- b = +HEAPF32[sp + 48 >> 2];
- a = x + y;
- b = y * x;
- a = c(1 + a);
- a = c(2 + a);
- a = c(3 + a);
- a = c(4 + a);
- HEAP32[sp + 40 >> 2] = a;
- HEAPF32[sp + 48 >> 2] = b;
-}
function chain$0(sp) {
sp = sp | 0;
var helper$0 = 0;
diff --git a/tools/test-js-optimizer-si-output.js b/tools/test-js-optimizer-si-output.js
new file mode 100644
index 00000000..9ef5171c
--- /dev/null
+++ b/tools/test-js-optimizer-si-output.js
@@ -0,0 +1,197 @@
+function a() {
+ if (x ? y : 0) {
+ g();
+ }
+ if (x) {
+ if (y) {
+ g();
+ } else {
+ h();
+ }
+ }
+ if (x) {
+ if (y) {
+ g();
+ }
+ h();
+ }
+ if (x) {
+ if (y) {
+ g();
+ }
+ } else {
+ h();
+ }
+ if (x) {
+ return;
+ if (y) {
+ g();
+ }
+ }
+ if ((x ? y : 0) ? z : 0) {
+ g();
+ }
+ if (x) {
+ return;
+ if (y ? z : 0) {
+ g();
+ }
+ }
+ if (x ? y : 0) {
+ return;
+ if (z) {
+ g();
+ }
+ }
+ if (x ? y : 0) {
+ if (z) {
+ g();
+ }
+ f();
+ }
+ if (x) {
+ if (y ? z : 0) {
+ g();
+ }
+ f();
+ }
+ if (x ? (f(), x = x + 2 | 0, y) : 0) {
+ g();
+ }
+ if (x) {
+ f();
+ x = x + 2 | 0;
+ return;
+ if (y) {
+ g();
+ }
+ }
+ andNowForElses();
+ if (x ? y : 0) {
+ f();
+ } else {
+ label = 5;
+ }
+ if (x) {
+ if (y) {
+ f();
+ } else {
+ label = 5;
+ }
+ } else {
+ label = 6;
+ }
+ if (x) {
+ if (y) {
+ f();
+ } else {
+ label = 5;
+ }
+ }
+ if (x) {
+ if (y) {
+ f();
+ }
+ } else {
+ label = 5;
+ }
+ if (x) {
+ a = 5;
+ if (y) {
+ f();
+ }
+ } else {
+ label = 5;
+ }
+ fuseElses();
+ if (x ? y : 0) {
+ f();
+ } else {
+ a();
+ }
+ if (x ? y : 0) {
+ f();
+ } else {
+ label = 52;
+ }
+ if ((label | 0) == 62) {
+ label = 0;
+ a();
+ }
+ if (x ? y : 0) {
+ f();
+ } else {
+ a();
+ }
+ while (1) {
+ if (x ? y : 0) {
+ f();
+ } else {
+ label = 953;
+ }
+ if ((label | 0) == 953) {
+ a();
+ }
+ }
+ if (x ? y : 0) {
+ label = 54;
+ } else {
+ label = 54;
+ }
+ if ((label | 0) == 54) {
+ label = 0;
+ a();
+ }
+}
+function b() {
+ if (x) {
+ a();
+ } else {
+ label = 5;
+ }
+ if ((label | 0) == 5) {
+ label = 0;
+ a();
+ }
+}
+function c() {
+ label = x;
+ if (x ? y : 0) {
+ f();
+ } else {
+ label = 151;
+ }
+ if ((label | 0) == 151) {
+ label = 0;
+ a();
+ }
+}
+function d() {
+ if (x ? y : 0) {
+ f();
+ } else {
+ label = 251;
+ }
+ if ((label | 0) == 251) {
+ label = 0;
+ a();
+ }
+ if ((label | 0) == 251) {
+ a();
+ }
+}
+function e() {
+ if (x ? y : 0) {
+ f();
+ } else {
+ label = 351;
+ }
+ if ((label | 0) == 351) {
+ label = 0;
+ a();
+ }
+ if ((label | 0) == x) {
+ a();
+ }
+}
+
diff --git a/tools/test-js-optimizer-si.js b/tools/test-js-optimizer-si.js
new file mode 100644
index 00000000..04ceec4a
--- /dev/null
+++ b/tools/test-js-optimizer-si.js
@@ -0,0 +1,258 @@
+function a() {
+ if (x) {
+ if (y) {
+ g();
+ }
+ }
+ if (x) {
+ if (y) {
+ g();
+ } else {
+ h();
+ }
+ }
+ if (x) {
+ if (y) {
+ g();
+ }
+ h();
+ }
+ if (x) {
+ if (y) {
+ g();
+ }
+ } else {
+ h();
+ }
+ if (x) {
+ return;
+ if (y) {
+ g();
+ }
+ }
+ if (x) {
+ if (y) {
+ if (z) {
+ g();
+ }
+ }
+ }
+ if (x) {
+ return;
+ if (y) {
+ if (z) {
+ g();
+ }
+ }
+ }
+ if (x) {
+ if (y) {
+ return;
+ if (z) {
+ g();
+ }
+ }
+ }
+ if (x) {
+ if (y) {
+ if (z) {
+ g();
+ }
+ f();
+ }
+ }
+ if (x) {
+ if (y) {
+ if (z) {
+ g();
+ }
+ }
+ f();
+ }
+ if (x) {
+ f();
+ x = x + 2 | 0;
+ if (y) {
+ g();
+ }
+ }
+ if (x) {
+ f();
+ x = x + 2 | 0;
+ return;
+ if (y) {
+ g();
+ }
+ }
+ andNowForElses();
+ if (x) {
+ if (y) {
+ f();
+ } else {
+ label = 5;
+ }
+ } else {
+ label = 5;
+ }
+ if (x) {
+ if (y) {
+ f();
+ } else {
+ label = 5;
+ }
+ } else {
+ label = 6;
+ }
+ if (x) {
+ if (y) {
+ f();
+ } else {
+ label = 5;
+ }
+ }
+ if (x) {
+ if (y) {
+ f();
+ }
+ } else {
+ label = 5;
+ }
+ if (x) {
+ a = 5; // do not commify me
+ if (y) {
+ f();
+ }
+ } else {
+ label = 5;
+ }
+ fuseElses();
+ if (x) {
+ if (y) {
+ f();
+ } else {
+ label = 51;
+ }
+ } else {
+ label = 51;
+ }
+ if ((label|0) == 51) {
+ label = 0;
+ a();
+ }
+ if (x) {
+ if (y) {
+ f();
+ } else {
+ label = 52;
+ }
+ } else {
+ label = 52;
+ }
+ if ((label|0) == 62) {
+ label = 0;
+ a();
+ }
+ if (x) {
+ if (y) {
+ f();
+ } else {
+ label = 53;
+ }
+ } else {
+ label = 53;
+ }
+ if ((label|0) == 53) {
+ a();
+ }
+ while (1) {
+ if (x) {
+ if (y) {
+ f();
+ } else {
+ label = 953;
+ }
+ } else {
+ label = 953;
+ }
+ if ((label|0) == 953) {
+ a();
+ }
+ }
+ if (x) {
+ if (y) {
+ label = 54; // extra label setting, cannot fuse here
+ } else {
+ label = 54;
+ }
+ } else {
+ label = 54;
+ }
+ if ((label|0) == 54) {
+ label = 0;
+ a();
+ }
+}
+function b() {
+ if (x) { // will not be fused, since we did not eliminate with elses
+ a();
+ } else {
+ label = 5;
+ }
+ if ((label|0) == 5) {
+ label = 0;
+ a();
+ }
+}
+function c() {
+ label = x; // dynamic assign to label, suppresses label removal
+ if (x) {
+ if (y) {
+ f();
+ } else {
+ label = 151;
+ }
+ } else {
+ label = 151;
+ }
+ if ((label|0) == 151) {
+ label = 0;
+ a();
+ }
+}
+function d() {
+ if (x) {
+ if (y) {
+ f();
+ } else {
+ label = 251;
+ }
+ } else {
+ label = 251;
+ }
+ if ((label|0) == 251) {
+ label = 0;
+ a();
+ }
+ if ((label|0) == 251) { // extra check of label, suppresses label removal
+ a();
+ }
+}
+function e() {
+ if (x) {
+ if (y) {
+ f();
+ } else {
+ label = 351;
+ }
+ } else {
+ label = 351;
+ }
+ if ((label|0) == 351) {
+ label = 0;
+ a();
+ }
+ if ((label|0) == x) { // dynamic check of label, suppresses label removal
+ a();
+ }
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS: ["a", "b", "c", "d"]