From a6438529a4e86ff4f5e21e816d5c0e1afed20a8d Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Fri, 22 Sep 2017 09:18:05 -0700 Subject: [PATCH 1/2] Switching the WTF app and tracing code to use bazel instead of anvil. This makes it possible to build on modern machines. A lot of documentation is still out of date, though it was mostly broken before so it's not much worse. --- .bazelrc | 1 + .gitattributes | 4 + .gitignore | 3 + .gitmodules | 15 - BUILD | 59 ++ BUILD.anvil | 673 ------------------ LICENSE | 2 +- README.md | 32 +- WORKSPACE | 27 + anvil.bat | 3 - app/BUILD | 82 +++ app/BUILD.anvil | 93 --- app/graphicsreplay.html | 4 +- app/maindisplay-debug.html | 2 +- app/maindisplay.html | 4 +- assets/BUILD | 21 + assets/BUILD.anvil | 28 - assets/store/tile-small.png | Bin 75617 -> 14695 bytes bindings/js/BUILD | 206 ++++++ {shims => bindings/js}/wtf-trace-closure.js | 0 {shims => bindings/js}/wtf-trace-disabled.js | 0 {shims => bindings/js}/wtf-trace.js | 0 builddefs/BUILD | 30 + builddefs/concat_files.sh | 4 + builddefs/config.bzl | 62 ++ builddefs/embed_files.py | 36 + builddefs/file_rules.bzl | 118 +++ builddefs/less_rules.bzl | 93 +++ builddefs/packaging_rules.bzl | 66 ++ builddefs/strip_comments.py | 30 + extensions/wtf-injector-chrome/BUILD | 64 ++ extensions/wtf-injector-chrome/BUILD.anvil | 81 --- extensions/wtf-injector-chrome/injectedtab.js | 4 +- .../{manifest.json => manifest.template.json} | 2 +- extensions/wtf-injector-chrome/popup.html | 2 +- extensions/wtf-injector-firefox/package.json | 2 +- externs/BUILD | 16 + externs/chrome.js | 12 +- externs/node.js | 2 +- lint.sh | 3 + package.json | 28 +- scripts/prepare-publish.sh | 94 ++- ...ate-third-party.sh => publish-gh-pages.sh} | 53 +- scripts/setup.bat | 54 -- scripts/setup.sh | 143 ---- scripts/update-gh-pages.sh | 88 --- scripts/update-version.sh | 8 +- src/BUILD | 9 + src/wtf/BUILD | 19 + src/wtf/addon/BUILD | 23 + src/wtf/app/BUILD | 135 ++++ src/wtf/app/addonmanager.js | 21 +- src/wtf/app/documentview.js | 2 +- src/wtf/app/framepainter.js | 2 +- src/wtf/app/granularity.js | 13 +- src/wtf/app/helpdialog.soy | 2 +- src/wtf/app/loader.js | 7 +- src/wtf/app/maindisplay.js | 6 +- src/wtf/app/query/querypanel.js | 3 +- src/wtf/app/splashdialog.soy | 2 +- src/wtf/app/statusbar.soy | 2 +- src/wtf/app/tabbar.js | 7 +- src/wtf/app/toolbar.soy | 2 +- src/wtf/app/tracks/trackspanel.js | 2 +- src/wtf/data/BUILD | 25 + src/wtf/data/contextinfo.js | 3 +- src/wtf/db/BUILD | 73 ++ src/wtf/db/eventiterator.js | 2 +- src/wtf/db/eventtype.js | 3 +- src/wtf/db/filter.js | 11 +- src/wtf/db/healthinfo.js | 3 +- src/wtf/db/queryresult.js | 2 +- src/wtf/db/sources/callsdatasource.js | 2 +- src/wtf/db/sources/cpuprofiledatasource.js | 2 +- src/wtf/db/zone.js | 5 +- src/wtf/doc/BUILD | 26 + src/wtf/events/BUILD | 37 + src/wtf/events/keyboard.js | 2 +- src/wtf/hud/BUILD | 54 ++ src/wtf/hud/hud.js | 3 +- src/wtf/hud/overlay.less | 29 +- src/wtf/io/BUILD | 41 ++ src/wtf/io/cff/BUILD | 40 ++ src/wtf/io/drive.js | 12 +- src/wtf/io/transports/blobwritetransport.js | 2 +- src/wtf/ipc/BUILD | 24 + src/wtf/math/BUILD | 16 + src/wtf/pal/BUILD | 23 + src/wtf/pal/browserplatform.js | 3 +- src/wtf/remote/BUILD | 24 + src/wtf/replay/graphics/BUILD | 61 ++ src/wtf/replay/graphics/contextpool.js | 3 +- .../replay/graphics/highlightvisualizer.js | 5 +- src/wtf/replay/graphics/offscreensurface.js | 30 +- src/wtf/replay/graphics/overdrawsurface.js | 8 +- src/wtf/replay/graphics/program.js | 2 +- .../replay/graphics/skipcallsvisualizer.js | 3 +- src/wtf/replay/graphics/ui/BUILD | 76 ++ src/wtf/replay/graphics/ui/argumentsdialog.js | 8 +- src/wtf/replay/graphics/ui/canvasarea.js | 2 +- src/wtf/replay/graphics/ui/eventnavigator.js | 2 +- .../graphics/ui/eventnavigatorsource.js | 2 +- .../graphics/ui/eventnavigatortoolbar.js | 10 +- src/wtf/replay/graphics/ui/graphicspanel.js | 4 +- src/wtf/replay/graphics/ui/graphicstoolbar.js | 10 +- src/wtf/replay/graphics/ui/rangeseeker.js | 2 +- .../replay/graphics/ui/replayframepainter.js | 2 + src/wtf/replay/timetravel/BUILD | 54 ++ src/wtf/replay/timetravel/replaysession.js | 2 +- src/wtf/timing/BUILD | 24 + src/wtf/timing/util.js | 10 +- src/wtf/trace/BUILD | 82 +++ src/wtf/trace/events.js | 5 +- src/wtf/trace/eventtype.js | 3 +- src/wtf/trace/eventtypebuilder.js | 2 +- src/wtf/trace/instrument.js | 6 +- .../trace/providers/chromedebugprovider.js | 2 +- src/wtf/trace/providers/webglprovider.js | 36 +- src/wtf/trace/providers/websocketprovider.js | 4 +- src/wtf/trace/providers/webworkerprovider.js | 6 +- src/wtf/trace/providers/xhrprovider.js | 6 +- src/wtf/trace/tracemanager.js | 4 +- src/wtf/ui/BUILD | 246 +++++++ src/wtf/ui/color/color.js | 2 +- src/wtf/ui/control.js | 2 +- src/wtf/ui/errordialog.js | 2 +- src/wtf/ui/errordialog.soy | 2 +- src/wtf/ui/settingsdialog.js | 4 +- src/wtf/ui/styles/BUILD | 23 + src/wtf/ui/styles/common.less | 8 +- src/wtf/ui/styles/css3.less | 12 +- src/wtf/ui/styles/kennedy.less | 40 +- src/wtf/ui/styles/reset.less | 4 +- src/wtf/ui/virtualtable.less | 1 - src/wtf/ui/zoom/element.js | 2 +- src/wtf/util/BUILD | 22 + src/wtf/version.js | 6 +- src/wtf/wtf.js | 10 +- test/test-js.html | 4 +- third_party/BUILD | 11 + third_party/BUILD.anvil | 44 -- third_party/anvil-build | 1 - third_party/closure-compiler | 1 - third_party/closure-library | 1 - third_party/closure-stylesheets | 1 - third_party/closure-templates | 1 - third_party/d3/BUILD | 40 ++ third_party/d3/BUILD.anvil | 35 - wtfrc | 8 - 149 files changed, 2491 insertions(+), 1596 deletions(-) create mode 100644 .bazelrc create mode 100644 .gitattributes create mode 100644 BUILD delete mode 100644 BUILD.anvil create mode 100644 WORKSPACE delete mode 100644 anvil.bat create mode 100644 app/BUILD delete mode 100644 app/BUILD.anvil create mode 100644 assets/BUILD delete mode 100644 assets/BUILD.anvil create mode 100644 bindings/js/BUILD rename {shims => bindings/js}/wtf-trace-closure.js (100%) rename {shims => bindings/js}/wtf-trace-disabled.js (100%) rename {shims => bindings/js}/wtf-trace.js (100%) create mode 100644 builddefs/BUILD create mode 100755 builddefs/concat_files.sh create mode 100644 builddefs/config.bzl create mode 100644 builddefs/embed_files.py create mode 100644 builddefs/file_rules.bzl create mode 100644 builddefs/less_rules.bzl create mode 100644 builddefs/packaging_rules.bzl create mode 100644 builddefs/strip_comments.py create mode 100644 extensions/wtf-injector-chrome/BUILD delete mode 100644 extensions/wtf-injector-chrome/BUILD.anvil rename extensions/wtf-injector-chrome/{manifest.json => manifest.template.json} (99%) create mode 100644 externs/BUILD create mode 100755 lint.sh rename scripts/{update-third-party.sh => publish-gh-pages.sh} (52%) delete mode 100644 scripts/setup.bat delete mode 100755 scripts/setup.sh delete mode 100755 scripts/update-gh-pages.sh create mode 100644 src/BUILD create mode 100644 src/wtf/BUILD create mode 100644 src/wtf/addon/BUILD create mode 100644 src/wtf/app/BUILD create mode 100644 src/wtf/data/BUILD create mode 100644 src/wtf/db/BUILD create mode 100644 src/wtf/doc/BUILD create mode 100644 src/wtf/events/BUILD create mode 100644 src/wtf/hud/BUILD create mode 100644 src/wtf/io/BUILD create mode 100644 src/wtf/io/cff/BUILD create mode 100644 src/wtf/ipc/BUILD create mode 100644 src/wtf/math/BUILD create mode 100644 src/wtf/pal/BUILD create mode 100644 src/wtf/remote/BUILD create mode 100644 src/wtf/replay/graphics/BUILD create mode 100644 src/wtf/replay/graphics/ui/BUILD create mode 100644 src/wtf/replay/timetravel/BUILD create mode 100644 src/wtf/timing/BUILD create mode 100644 src/wtf/trace/BUILD create mode 100644 src/wtf/ui/BUILD create mode 100644 src/wtf/ui/styles/BUILD create mode 100644 src/wtf/util/BUILD create mode 100644 third_party/BUILD delete mode 100644 third_party/BUILD.anvil delete mode 160000 third_party/anvil-build delete mode 160000 third_party/closure-compiler delete mode 160000 third_party/closure-library delete mode 160000 third_party/closure-stylesheets delete mode 160000 third_party/closure-templates create mode 100644 third_party/d3/BUILD delete mode 100644 third_party/d3/BUILD.anvil delete mode 100644 wtfrc diff --git a/.bazelrc b/.bazelrc new file mode 100644 index 00000000..346f9888 --- /dev/null +++ b/.bazelrc @@ -0,0 +1 @@ +build --strategy=Closure=worker diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..7a61a332 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +* text=auto whitespace=blank-at-eol,tab-in-indent,trailing-space,tabwidth=2 + +*.bat text eol=crlf +*.sh text eol=lf diff --git a/.gitignore b/.gitignore index e2c7c1a4..726beb74 100644 --- a/.gitignore +++ b/.gitignore @@ -56,6 +56,7 @@ private/ node_modules/ node_modules/**/build/ node_modules/.bin/ +yarn.lock # coverage/etc scratch/ @@ -67,6 +68,8 @@ build-gen/ build-bin/ build-test/ +bazel-* + # ============================================================================== # WTF # ============================================================================== diff --git a/.gitmodules b/.gitmodules index edca41f3..9cf820da 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,24 +1,9 @@ -[submodule "third_party/closure-compiler"] - path = third_party/closure-compiler - url = https://github.com/benvanik/google-closure-compiler-bin.git -[submodule "third_party/closure-templates"] - path = third_party/closure-templates - url = https://github.com/benvanik/google-closure-templates-bin.git -[submodule "third_party/closure-stylesheets"] - path = third_party/closure-stylesheets - url = https://github.com/benvanik/google-closure-stylesheets-bin.git -[submodule "third_party/anvil-build"] - path = third_party/anvil-build - url = https://github.com/google/anvil-build.git [submodule "third_party/closure-linter"] path = third_party/closure-linter url = https://github.com/knutwalker/google-closure-linter.git [submodule "third_party/normalize.css"] path = third_party/normalize.css url = https://github.com/necolas/normalize.css.git -[submodule "third_party/closure-library"] - path = third_party/closure-library - url = https://github.com/google/closure-library.git [submodule "third_party/firefox-addon-sdk"] path = third_party/firefox-addon-sdk url = https://github.com/mozilla/addon-sdk.git diff --git a/BUILD b/BUILD new file mode 100644 index 00000000..35a3d979 --- /dev/null +++ b/BUILD @@ -0,0 +1,59 @@ +# Description: +# Javascript and C++ instrumentation-based profiling tools. +# https://github.com/google/tracing-framework + +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) # BSD 3-clause + +exports_files(["LICENSE"]) + +# Restricts most packages to use by WTF only. +# Selective rules we wish to support to external users are set to +# //visibility:public. +package_group( + name = "internal", + packages = [ + "//addons/...", + "//app/...", + "//assets/...", + "//bin/...", + "//bindings/...", + "//extensions/...", + "//externs/...", + "//src/...", + "//test/...", + "//third_party/...", + ], +) + +# Exported for nodejs rules. +filegroup( + name = "node_modules", + srcs = glob(["node_modules/**/*"]), +) + +# Debug; all runtime checks, verbose logging, and debug symbols. +# +# $ bazel build -c dbg ... +config_setting( + name = "dbg", + values = {"compilation_mode": "dbg"}, +) + +# Fast build; some runtime checks, detailed logging, and debug symbols. +# This is the default and will likely be what you want to use unless profiling. +# +# $ bazel build -c fastbuild ... +config_setting( + name = "fastbuild", + values = {"compilation_mode": "fastbuild"}, +) + +# Optimized; no runtime checks and terse logging. +# +# $ bazel build -c opt ... +config_setting( + name = "opt", + values = {"compilation_mode": "opt"}, +) diff --git a/BUILD.anvil b/BUILD.anvil deleted file mode 100644 index 85f9e077..00000000 --- a/BUILD.anvil +++ /dev/null @@ -1,673 +0,0 @@ -# Copyright 2012 Google Inc. All Rights Reserved. - -__author__ = 'benvanik@google.com (Ben Vanik)' - - -# Master anvil-build BUILD file for wtf - - -# ------------------------------------------------------------------------------ -# Third Party -# ------------------------------------------------------------------------------ - -JS_COMPILER_JAR='third_party:closure_compiler_jar' -SOY_COMPILER_JAR='third_party:closure_templates_jar' -GSS_COMPILER_JAR='third_party:closure_stylesheets_jar' -file_set( - name='all_externs', - srcs=glob('externs/**/*.js')) - - -# ------------------------------------------------------------------------------ -# CSS -# ------------------------------------------------------------------------------ - -file_set( - name='all_less', - srcs=glob('src/**/*.less')) - - -# ------------------------------------------------------------------------------ -# Soy Templates -# ------------------------------------------------------------------------------ - -closure_soy_library( - name='wtf_ui_soy_js', - srcs=glob('src/wtf/ui/**/*.soy'), - compiler_jar=SOY_COMPILER_JAR) -closure_soy_library( - name='wtf_hud_soy_js', - srcs=glob('src/wtf/hud/**/*.soy'), - deps=':wtf_ui_soy_js', - compiler_jar=SOY_COMPILER_JAR) -closure_soy_library( - name='wtf_app_soy_js', - srcs=glob('src/wtf/app/**/*.soy'), - deps=':wtf_ui_soy_js', - compiler_jar=SOY_COMPILER_JAR) -closure_soy_library( - name='wtf_replay_soy_js', - srcs=glob('src/wtf/replay/**/*.soy'), - deps=':wtf_ui_soy_js', - compiler_jar=SOY_COMPILER_JAR) - -file_set( - name='all_soy_js', - srcs=[ - ':wtf_ui_soy_js', - ':wtf_hud_soy_js', - ':wtf_app_soy_js', - ':wtf_replay_soy_js', - ]) - - -# ------------------------------------------------------------------------------ -# JavaScript -# ------------------------------------------------------------------------------ - -# wtf src/ -file_set( - name='wtf_src_js', - srcs=glob('src/**/*.js')) - -# third_party js -file_set( - name='third_party_js', - srcs=[ - 'third_party/d3/colorbrewer.js', - ]) - -# Testing support files -file_set( - name='testing_files', - srcs=[ - 'node_modules/mocha/mocha.js', - 'node_modules/mocha/mocha.css', - 'node_modules/chai/chai.js', - ]) - -SHARED_JS_FLAGS=[ - '--summary_detail_level=3', - '--define=goog.DEBUG=false', - '--define=goog.asserts.ENABLE_ASSERTS=false', - '--create_source_map=%outname%.map', - '--source_map_format=V3', - ] - -RELEASE_JS_FLAGS=[ - '--use_types_for_optimization', - #'--collapse_variable_declarations', - #'--collapse_anonymous_functions', - #'--collapse_properties', - #'--disambiguate_properties', - # rewrites things to be smaller but likely not better - # http://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/javascript/jscomp/FunctionRewriter.java - #'--rewrite_function_expressions=false', - # slow - may want disabled - #'--devirtualize_prototype_methods', - #'--devirtualize_prototype_methods=false', - ] - -# deps.js only -closure_js_library( - name='wtf_js_deps', - out='wtf_js', - mode='DEPS', - entry_points=[ - 'wtf.app.exports', - 'wtf.db.exports', - 'wtf.hud.exports', - 'wtf.remote.exports', - 'wtf.replay.graphics.exports', - 'wtf.replay.timeTravel.exports', - 'wtf.trace.exports', - ], - srcs=[ - 'third_party:all_closure_js', - ':third_party_js', - ':wtf_src_js', - ':all_soy_js', - ], - compiler_jar=JS_COMPILER_JAR) - - -# ------------------------------------------------------------------------------ -# JavaScript : trace_web -# ------------------------------------------------------------------------------ - -less_css_library( - name='wtf_trace_web_styles_debug', - srcs=['src/wtf/hud/hud.less', ':all_less'], - include_paths=['src',], - compiler_flags=[]) -less_css_library( - name='wtf_trace_web_styles_release_unminified', - srcs=['src/wtf/hud/hud.less', ':all_less'], - include_paths=['src',], - compiler_flags=['']) -closure_gss_library( - name='wtf_trace_web_styles_release', - mode='COMPILED', - srcs=':wtf_trace_web_styles_release_unminified', - compiler_jar=GSS_COMPILER_JAR, - compiler_flags=[ - '--css-renaming-prefix', 'wtf_', - '--no-eliminate-dead-styles', - '--allow-unrecognized-functions', - ]) - -WTF_TRACE_WEB_SRCS=[ - ':third_party_js', - ':wtf_src_js', - ':wtf_ui_soy_js', - ':wtf_hud_soy_js', - ] - -# All uncompiled JS -file_set( - name='all_trace_web_uncompiled_js', - srcs=[ - 'third_party:all_closure_js', - 'third_party:closure_uncompiled_js', - ] + WTF_TRACE_WEB_SRCS) - -# All compiled JS -file_set( - name='all_trace_web_compiled_js', - srcs=[ - 'third_party:all_closure_js', - ':wtf_trace_web_styles_release', - ] + WTF_TRACE_WEB_SRCS) - -WTF_TRACE_WEB_ENTRY_POINTS=[ - 'wtf.addon', - 'wtf.trace.exports', - 'wtf.hud.exports', - 'wtf.remote.exports', - ] - -WTF_TRACE_WEB_JS_FLAGS=[ - '--language_in=ECMASCRIPT5_STRICT', - '--define=wtf.hud.exports.ENABLE_EXPORTS=true', - '--define=wtf.remote.exports.ENABLE_EXPORTS=true', - '--define=wtf.trace.exports.ENABLE_EXPORTS=true', - '--output_wrapper="if(!this.wtf){(function(){%output%}).call(this);}"', - ] -# wtf_trace_web_js_compiled_debug js -closure_js_library( - name='wtf_trace_web_js_compiled_debug_head', - deps_out='wtf_trace_web_js', - mode='ADVANCED', - pretty_print=True, - entry_points=WTF_TRACE_WEB_ENTRY_POINTS, - srcs=':all_trace_web_uncompiled_js', - externs=[':all_externs'], - compiler_jar=JS_COMPILER_JAR, - compiler_flags=SHARED_JS_FLAGS + - WTF_TRACE_WEB_JS_FLAGS + [ - '--debug', - '--create_name_map_files', - ]) -# wtf_trace_web_js_compiled js -closure_js_library( - name='wtf_trace_web_js_compiled_head', - deps_out='wtf_trace_web_js', - mode='ADVANCED', - entry_points=WTF_TRACE_WEB_ENTRY_POINTS, - srcs=':all_trace_web_compiled_js', - externs=[':all_externs'], - compiler_jar=JS_COMPILER_JAR, - compiler_flags=SHARED_JS_FLAGS + RELEASE_JS_FLAGS + - WTF_TRACE_WEB_JS_FLAGS + [ - ]) - -# TODO(benvanik): test in IE -STYLES_WRAPPER=''.join([ - "if(this.window && !window.wtf_styles){window.wtf_styles=1;", - "var style = document.createElement('style');", - "style.innerHTML = '%output%';", - "(document.documentElement || document.head).appendChild(style);", - ";}\n"]) -# wtf_trace_web_js_compiled_debug -embed_files( - name='wtf_trace_web_styles_debug_js', - srcs=[':wtf_trace_web_styles_debug'], - wrapper=STYLES_WRAPPER, - replace_chars=[['\n', '\\n'], ['\'', '\\\'']]) -concat_files( - name='wtf_trace_web_js_compiled_debug', - out='wtf_trace_web_js_compiled_debug.js', - srcs=[ - ':wtf_trace_web_js_compiled_debug_head', - ':wtf_trace_web_styles_debug_js', - 'assets:hud_icons', - ], - src_exclude_filter='*-deps.js') -# wtf_trace_web_js_compiled -embed_files( - name='wtf_trace_web_styles_release_js', - srcs=[':wtf_trace_web_styles_release'], - wrapper=STYLES_WRAPPER, - replace_chars=[['\n', '\\n'], ['\'', '\\\'']], - src_exclude_filter='*.js') -concat_files( - name='wtf_trace_web_js_compiled', - out='wtf_trace_web_js_compiled.js', - srcs=[ - ':wtf_trace_web_js_compiled_head', - ':wtf_trace_web_styles_release_js', - 'assets:hud_icons', - ], - src_exclude_filter='*-deps.js') - -file_set( - name='wtf_trace_web_debug', - srcs=[ - ':testing_files', - ':wtf_trace_web_js_compiled_debug', - ':wtf_trace_web_styles_debug', - ]) -file_set( - name='wtf_trace_web_release', - srcs=[ - ':wtf_trace_web_js_compiled', - ], - src_exclude_filter='*-deps.js') - - -# ------------------------------------------------------------------------------ -# JavaScript : trace_min -# ------------------------------------------------------------------------------ - -WTF_TRACE_MIN_SRCS=[ - ':third_party_js', - ':wtf_src_js', - ] - -# All uncompiled JS -file_set( - name='all_trace_min_uncompiled_js', - srcs=[ - 'third_party:all_closure_js', - 'third_party:closure_uncompiled_js', - ] + WTF_TRACE_MIN_SRCS) - -# All compiled JS -file_set( - name='all_trace_min_compiled_js', - srcs=[ - 'third_party:all_closure_js', - ] + WTF_TRACE_MIN_SRCS) - -WTF_TRACE_MIN_ENTRY_POINTS=[ - 'wtf.trace.exports', - ] - -WTF_TRACE_MIN_JS_FLAGS=[ - '--language_in=ECMASCRIPT5_STRICT', - '--define=wtf.MIN_BUILD=true', - '--define=wtf.trace.exports.ENABLE_EXPORTS=true', - '--output_wrapper="if(!this.wtf){(function(){%output%}).call(this);}"', - ] -# wtf_trace_min_js_compiled_debug js -closure_js_library( - name='wtf_trace_min_js_compiled_debug', - deps_out='wtf_trace_min_js', - mode='ADVANCED', - pretty_print=True, - entry_points=WTF_TRACE_MIN_ENTRY_POINTS, - srcs=':all_trace_min_uncompiled_js', - externs=[':all_externs'], - compiler_jar=JS_COMPILER_JAR, - compiler_flags=SHARED_JS_FLAGS + - WTF_TRACE_MIN_JS_FLAGS + [ - '--debug', - '--create_name_map_files', - ]) -# wtf_trace_min_js_compiled js -closure_js_library( - name='wtf_trace_min_js_compiled', - deps_out='wtf_trace_min_js', - mode='ADVANCED', - entry_points=WTF_TRACE_MIN_ENTRY_POINTS, - srcs=':all_trace_min_compiled_js', - externs=[':all_externs'], - compiler_jar=JS_COMPILER_JAR, - compiler_flags=SHARED_JS_FLAGS + RELEASE_JS_FLAGS + - WTF_TRACE_MIN_JS_FLAGS + [ - ]) - -file_set( - name='wtf_trace_min_debug', - srcs=[ - ':testing_files', - ':wtf_trace_min_js_compiled_debug', - ]) -file_set( - name='wtf_trace_min_release', - srcs=[ - ':wtf_trace_min_js_compiled', - ], - src_exclude_filter='*-deps.js') - - -# ------------------------------------------------------------------------------ -# JavaScript : trace_prod -# ------------------------------------------------------------------------------ - -WTF_TRACE_PROD_JS_FLAGS=[ - '--language_in=ECMASCRIPT5_STRICT', - '--define=wtf.MIN_BUILD=true', - '--define=wtf.PROD_BUILD=true', - '--define=wtf.trace.exports.ENABLE_EXPORTS=true', - '--output_wrapper="if(!this.wtf){(function(){%output%}).call(this);}"', - ] -# wtf_trace_prod_js_compiled_debug js -closure_js_library( - name='wtf_trace_prod_js_compiled_debug', - deps_out='wtf_trace_prod_js', - mode='ADVANCED', - pretty_print=True, - entry_points=WTF_TRACE_MIN_ENTRY_POINTS, - srcs=':all_trace_min_uncompiled_js', - externs=[':all_externs'], - compiler_jar=JS_COMPILER_JAR, - compiler_flags=SHARED_JS_FLAGS + - WTF_TRACE_PROD_JS_FLAGS + [ - '--debug', - '--create_name_map_files', - ]) -# wtf_trace_prod_js_compiled js -closure_js_library( - name='wtf_trace_prod_js_compiled', - deps_out='wtf_trace_prod_js', - mode='ADVANCED', - entry_points=WTF_TRACE_MIN_ENTRY_POINTS, - srcs=':all_trace_min_compiled_js', - externs=[':all_externs'], - compiler_jar=JS_COMPILER_JAR, - compiler_flags=SHARED_JS_FLAGS + RELEASE_JS_FLAGS + - WTF_TRACE_PROD_JS_FLAGS + [ - ]) - -file_set( - name='wtf_trace_prod_debug', - srcs=[ - ':wtf_trace_prod_js_compiled_debug', - ]) -file_set( - name='wtf_trace_prod_release', - srcs=[ - ':wtf_trace_prod_js_compiled', - ], - src_exclude_filter='*-deps.js') - - -# ------------------------------------------------------------------------------ -# JavaScript : node -# ------------------------------------------------------------------------------ - -closure_js_library( - name='wtf_node_js_compiled', - deps_out='wtf_node_js', - # TODO(benvanik): fix exports so ADVANCED can be used - mode='SIMPLE', - pretty_print=True, - entry_points=[ - 'wtf.db.exports', - 'wtf.db.node', - 'wtf.replay.graphics.Step', - 'wtf.trace.exports', - 'wtf.trace.node', - ], - srcs=[ - 'third_party:all_closure_js', - ':third_party_js', - ':wtf_src_js', - ], - externs=[':all_externs'], - compiler_jar=JS_COMPILER_JAR, - compiler_flags=SHARED_JS_FLAGS + RELEASE_JS_FLAGS + [ - '--language_in=ECMASCRIPT5_STRICT', - '--define=goog.DEBUG=false', - '--define=goog.asserts.ENABLE_ASSERTS=false', - '--define=wtf.NODE=true', - '--define=wtf.db.exports.ENABLE_EXPORTS=true', - '--define=wtf.trace.exports.ENABLE_EXPORTS=true', - '--output_wrapper="module.exports = (function(exports){%output%;return this.wtf;}).call(global); delete global.wtf;"', - ]) - -file_set( - name='wtf_node_release', - srcs=[ - ':wtf_node_js_compiled', - ], - src_exclude_filter='*-deps.js') - - -# ------------------------------------------------------------------------------ -# JavaScript : app -# ------------------------------------------------------------------------------ -# TODO(benvanik): move to app/BUILD? - -WTF_UI_SRCS=[ - ':third_party_js', - ':wtf_src_js', - ':wtf_ui_soy_js', - ':wtf_app_soy_js', - ':wtf_replay_soy_js', - ] - -# All uncompiled JS -file_set( - name='all_ui_uncompiled_js', - srcs=[ - 'third_party:all_closure_js', - 'third_party:closure_uncompiled_js', - 'app:debug_js_srcs', - ] + WTF_UI_SRCS) - -# All compiled JS -file_set( - name='all_ui_compiled_js', - srcs=[ - 'third_party:all_closure_js', - 'app:release_js_srcs', - ] + WTF_UI_SRCS) - -WTF_UI_ENTRY_POINTS=[ - 'wtf.app.exports', - 'wtf.db.exports', - 'wtf.replay.graphics.exports', - 'wtf.replay.timeTravel.exports', - ] - -WTF_UI_JS_FLAGS=[ - # Safari has issues with 'use strict'. - '--language_in=ECMASCRIPT5', - ] - -# wtf_ui_js_uncompiled -closure_js_library( - name='wtf_ui_js_uncompiled', - deps_out='wtf_ui_js', - mode='UNCOMPILED', - entry_points=WTF_UI_ENTRY_POINTS, - srcs=':all_ui_uncompiled_js', - externs=[':all_externs'], - compiler_jar=JS_COMPILER_JAR, - compiler_flags=SHARED_JS_FLAGS + WTF_UI_JS_FLAGS + [ - ], - wrap_with_global='this', - file_list_out='wtf_ui_js.list') - -# wtf_ui_js_compiled -closure_js_library( - name='wtf_ui_js_compiled', - deps_out='wtf_ui_js', - mode='ADVANCED', - #pretty_print=True, - entry_points=WTF_UI_ENTRY_POINTS, - srcs=':all_ui_compiled_js', - externs=[':all_externs'], - compiler_jar=JS_COMPILER_JAR, - compiler_flags=SHARED_JS_FLAGS + RELEASE_JS_FLAGS + WTF_UI_JS_FLAGS + [ - #'--debug', - '--define=wtf.app.exports.ENABLE_EXPORTS=true', - '--define=wtf.db.exports.ENABLE_EXPORTS=true', - '--define=wtf.replay.graphics.exports.ENABLE_EXPORTS=true', - '--define=wtf.replay.timeTravel.exports.ENABLE_EXPORTS=true', - ], - wrap_with_global='window') - -less_css_library( - name='wtf_ui_styles_debug', - srcs=['src/wtf/app/app.less', ':all_less'], - include_paths=['src',], - compiler_flags=[]) -less_css_library( - name='wtf_ui_styles_release', - srcs=['src/wtf/app/app.less', ':all_less'], - include_paths=['src',], - compiler_flags=['--compress']) - - -# ------------------------------------------------------------------------------ -# JS linting -# ------------------------------------------------------------------------------ - -CLOSURE_LINTER_PATH='third_party/closure-linter/' - -file_set( - name='lint_src', - srcs=[':wtf_src_js'], - src_exclude_filter='filterparser.js|eventbuffers_test.js|benchmark.js') - -closure_js_fixstyle( - name='all_js_fixstyle', - namespaces=['goog', 'wtf',], - srcs=[':lint_src'], - linter_path=CLOSURE_LINTER_PATH) - -closure_js_lint( - name='all_js_lint', - namespaces=['goog', 'wtf',], - srcs=[':lint_src'], - linter_path=CLOSURE_LINTER_PATH) - - -# ------------------------------------------------------------------------------ -# Testing -# ------------------------------------------------------------------------------ - -TEST_REQUIRES=[ - ('node module', 'mocha@1.4.2'), - ('node module', 'chai@1.2.0'), - ] - -file_set( - name='test_external', - deps=[':fast'], - requires=TEST_REQUIRES) - -shell_execute( - name='wtf_js_test', - srcs=glob('src/**/*_test.js'), - deps=[':fast'], - command=[ - 'node_modules/mocha/bin/mocha', - '--ui', 'tdd', - '--reporter', 'dot', - '--require', 'src/wtf/bootstrap/mocha.js', - ], - requires=TEST_REQUIRES) - - -# ------------------------------------------------------------------------------ -# Target rules -# ------------------------------------------------------------------------------ - -file_set( - name='setup', - requires=[ - ('node', '>=0.6.14'), - ] + TEST_REQUIRES) - -file_set( - name='lint', - deps=':all_js_lint') - -file_set( - name='fixstyle', - deps=':all_js_fixstyle') - -file_set( - name='fast', - deps=[ - ':testing_files', - ':wtf_js_deps', - ':wtf_trace_web_styles_debug', - 'app:fast', - ]) - -file_set( - name='deps', - srcs=[ - ':wtf_js_deps', - ]) - -file_set( - name='test', - deps=[':wtf_js_test']) - -file_set( - name='debug', - srcs=[ - ':testing_files', - ':deps', - ':wtf_trace_web_debug', - 'app:debug', - ]) - -archive_files( - name='wtf-trace-web-api', - srcs=[ - ':wtf_trace_web_release', - ] + glob('shims/*.*')) - -archive_files( - name='wtf-app', - srcs=[ - 'app:release', - ]) - -file_set( - name='injector', - srcs=[ - 'extensions/wtf-injector-chrome:wtf-injector-chrome', - 'extensions/wtf-injector-firefox:wtf-injector-firefox', - ]) - -file_set( - name='release', - srcs=[ - ':deps', - ':wtf_trace_web_release', - ':wtf_trace_min_release', - ':wtf_node_release', - 'app:release', - ':wtf-trace-web-api', - ':wtf-app', - ':injector', - ], - src_exclude_filter='*-deps.js', - deps=[ - #':all_js_lint', - ]) - -file_set( - name='gh-pages', - srcs=[ - ':wtf_trace_web_release', - 'app:app_files' - ]) diff --git a/LICENSE b/LICENSE index 989d02e4..3bd62820 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright 2012, Google Inc. +Copyright 2017, Google Inc. All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.md b/README.md index bf0388e9..d9df771a 100644 --- a/README.md +++ b/README.md @@ -26,27 +26,33 @@ See [building](https://github.com/google/tracing-framework/blob/master/docs/buil ## Quickstart -Just want the extension as fast as possible? +For more information and other build options, see [building](https://github.com/google/tracing-framework/blob/master/docs/building.md). + +### Setup ```bash -umask 0022 -git clone https://github.com/google/tracing-framework.git -cd tracing-framework/ -./scripts/setup.sh # or setup.bat on Windows -source wtfrc && deployext -# At chrome://extensions load unpacked extension from -# build-bin/wtf-injector-chrome +$ git clone --recursive https://github.com/google/tracing-framework.git && cd tracing-framework/ +$ bazel run @yarn//:yarn ``` -If you pull new changes, just redeploy: +### Chrome Extension + +This builds and copies the unpacked extension to +`bazel-bin/extensions/wtf-injector-chrome/wtf-injector-chrome_pkg`, which can +then be loaded from chrome://extensions. ```bash -git pull && git submodule update -deployext -# Reload from chrome://extensions +$ bazel build -c opt //extensions/wtf-injector-chrome ``` -For more information and other build options, see [building](https://github.com/google/tracing-framework/blob/master/docs/building.md). +### Embedded Tracing Library + +This produces `bazel-bin/bindings/js/wtf_trace_web_js_compiled.js`, which can be +copied and included via script tags on any page. + +```bash +$ bazel build -c opt //bindings/js:wtf_trace_web_js +``` ## License diff --git a/WORKSPACE b/WORKSPACE new file mode 100644 index 00000000..ca5f08cb --- /dev/null +++ b/WORKSPACE @@ -0,0 +1,27 @@ +workspace(name = "tracing_framework") + +http_archive( + name = "io_bazel_rules_closure", + strip_prefix = "rules_closure-0.4.2", + sha256 = "25f5399f18d8bf9ce435f85c6bbf671ec4820bc4396b3022cc5dc4bc66303609", + urls = [ + "http://mirror.bazel.build/github.com/bazelbuild/rules_closure/archive/0.4.2.tar.gz", + "https://github.com/bazelbuild/rules_closure/archive/0.4.2.tar.gz", + ], +) + +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_repositories") + +closure_repositories() + +load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") + +git_repository( + name = "build_bazel_rules_nodejs", + remote = "https://github.com/bazelbuild/rules_nodejs.git", + tag = "0.1.6", +) + +load("@build_bazel_rules_nodejs//:defs.bzl", "node_repositories") + +node_repositories(package_json = ["//:package.json"]) diff --git a/anvil.bat b/anvil.bat deleted file mode 100644 index 1912edfc..00000000 --- a/anvil.bat +++ /dev/null @@ -1,3 +0,0 @@ -@ECHO OFF - -third_party\anvil-build\anvil-local.bat %* diff --git a/app/BUILD b/app/BUILD new file mode 100644 index 00000000..8b4d204d --- /dev/null +++ b/app/BUILD @@ -0,0 +1,82 @@ +# Description: +# WTF Javascript app UI. + +package(default_visibility = ["//:internal"]) + +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_css_binary") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_binary") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library") +load("//builddefs:config.bzl", "SHARED_CSS_FLAGS") +load("//builddefs:config.bzl", "SHARED_JS_FLAGS") +load("//builddefs:packaging_rules.bzl", "pkg_zip") + +filegroup( + name = "icons", + srcs = glob(["icons/**"]), +) + +closure_js_binary( + name = "wtf_ui_js_compiled", + defs = SHARED_JS_FLAGS + [ + "--define=wtf.app.exports.ENABLE_EXPORTS=true", + "--define=wtf.db.exports.ENABLE_EXPORTS=true", + "--define=wtf.replay.graphics.exports.ENABLE_EXPORTS=true", + "--define=wtf.replay.timeTravel.exports.ENABLE_EXPORTS=true", + ], + dependency_mode = "STRICT", + entry_points = [ + "wtf.app.exports", + "wtf.db.exports", + "wtf.replay.graphics.exports", + "wtf.replay.timeTravel.exports", + ], + language = "ECMASCRIPT5_STRICT", + output_wrapper = "(function(){%output%}).call(window)", + deps = [ + "//src/wtf/addon", + "//src/wtf/app", + "//src/wtf/db", + "@io_bazel_rules_closure//closure/library", + ], +) + +closure_css_binary( + name = "wtf_ui_styles", + defs = SHARED_CSS_FLAGS, + renaming = False, + deps = ["//src/wtf/app:app_styles"], +) + +filegroup( + name = "app", + srcs = select({ + "//:dbg": [ + # TODO(benvanik): exploded mode. + "maindisplay-debug.html", + "scripts/debug-prefix.js", + "scripts/debug-postfix-maindisplay.js", + "//third_party/d3:min", + ":wtf_ui_styles", + ], + "//:fastbuild": [ + ":wtf_ui_js_compiled", + "maindisplay-debug.html", + ":wtf_ui_styles", + ":icons", + "//third_party/d3:min", + ], + "//:opt": [ + ":wtf_ui_js_compiled", + ":icons", + "maindisplay.html", + "scripts/release-maindisplay.js", + "//third_party/d3:min", + ":wtf_ui_styles", + ], + }), +) + +pkg_zip( + name = "wtf-app", + srcs = [":app",], +) diff --git a/app/BUILD.anvil b/app/BUILD.anvil deleted file mode 100644 index 3914440b..00000000 --- a/app/BUILD.anvil +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright 2012 Google Inc. All Rights Reserved. - -__author__ = 'benvanik@google.com (Ben Vanik)' - - -# Chrome app release output - - -# ------------------------------------------------------------------------------ -# JavaScript -# ------------------------------------------------------------------------------ - -file_set( - name='shared_js_srcs', - srcs=[ - # soy - ]) - -file_set( - name='debug_js_srcs', - srcs=[ - ':shared_js_srcs', - ]) -file_set( - name='release_js_srcs', - srcs=[ - ':shared_js_srcs', - ]) - -file_set( - name='third_party_js', - srcs=[ - '../third_party/d3:min', - ]) - - -# ------------------------------------------------------------------------------ -# Modes -# ------------------------------------------------------------------------------ - -file_set( - name='exploded', - srcs=[ - '../third_party:all_closure_js', - '..:wtf_src_js', - '..:all_soy_js', - '..:deps', - '..:wtf_ui_styles_debug', - 'maindisplay-debug.html', - 'scripts/debug-prefix.js', - 'scripts/debug-postfix-maindisplay.js', - ':third_party_js', - ]) - -file_set( - name='debug', - srcs=[ - '..:wtf_ui_js_uncompiled', - '..:wtf_ui_styles_debug', - 'maindisplay-debug.html', - ':third_party_js', - ]) - -file_set( - name='release', - srcs=[ - '..:wtf_ui_js_compiled', - '..:wtf_ui_styles_release', - 'maindisplay.html', - 'scripts/release-maindisplay.js', - ':third_party_js', - ] + glob('icons/**/*'), - src_exclude_filter='*-deps.js') - -file_set( - name='fast', - srcs=[ - '..:wtf_ui_styles_debug', - ]) - -file_set( - name='app_files', - srcs=[ - 'maindisplay.html', - 'scripts/release-maindisplay.js', - - ':third_party_js', - - # This is bad - should prefix this. - '..:wtf_ui_styles_release', - '..:wtf_ui_js_compiled', - ] + glob('icons/**/*'), - src_exclude_filter='*-deps.js') diff --git a/app/graphicsreplay.html b/app/graphicsreplay.html index fdeb5763..059d6889 100644 --- a/app/graphicsreplay.html +++ b/app/graphicsreplay.html @@ -3,8 +3,8 @@ WTF Graphics Replay - - + +
diff --git a/app/maindisplay-debug.html b/app/maindisplay-debug.html index db5dc076..585a06b8 100644 --- a/app/maindisplay-debug.html +++ b/app/maindisplay-debug.html @@ -5,7 +5,7 @@ - + diff --git a/app/maindisplay.html b/app/maindisplay.html index b9a0772d..2060aa06 100644 --- a/app/maindisplay.html +++ b/app/maindisplay.html @@ -3,8 +3,8 @@ Web Tracing Framework - - + + diff --git a/assets/BUILD b/assets/BUILD new file mode 100644 index 00000000..c68e2c9e --- /dev/null +++ b/assets/BUILD @@ -0,0 +1,21 @@ +# Description: +# Assets such as icons and images. + +package(default_visibility = ["//:internal"]) + +load("//builddefs:file_rules.bzl", "concat_files", "embed_files") + +ICON_WRAPPER = "".join([ + "wtf.ui.icons.setIconData(", + "'/%path%', ", + "'data:image/png;base64,%output%'", + ");\n", +]) + +embed_files( + name = "hud_icons", + srcs = glob(["icons/**/*.png"]), + out = "hud_icons.js", + encoding = "base64", + wrapper = ICON_WRAPPER, +) diff --git a/assets/BUILD.anvil b/assets/BUILD.anvil deleted file mode 100644 index 36f3e6d4..00000000 --- a/assets/BUILD.anvil +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2012 Google Inc. All Rights Reserved. - -__author__ = 'benvanik@google.com (Ben Vanik)' - - -# Assets rules. -# These are used to build javascript code with embedded data assets. - - -ICON_WRAPPER=''.join([ - "wtf.ui.icons.setIconData(", - "'/assets/%path%', ", - "'data:image/png;base64,%output%'", - ");\n" - ]) - - -# ------------------------------------------------------------------------------ -# HUD -# ------------------------------------------------------------------------------ - -embed_files( - name='hud_icons', - srcs=[ - ] + glob('icons/*.png'), - wrapper=ICON_WRAPPER, - encoding='base64', - replace_chars=[['\n', '\\n'], ['\'', '\\\'']]) diff --git a/assets/store/tile-small.png b/assets/store/tile-small.png index 4171bbb0433a628ad7fa048240600665096f635e..939c6a5527ccbba49cbc617b8ee78347ffcbf434 100644 GIT binary patch literal 14695 zcmcJ$Wl$VIxGgx42@C|8!8O3(5;V9526uN07ThH`!3GT)+#v)F!QI{6-3jgvn|t5Z z+pV|vRox$NfAp!Y?&_*jU0;3u_36`LN(z$bs6?m$003QD3Z?=8Ak_bB1R%tJBRSPB z|1p8$Af@dL0KCKgkA?t9O(y^V$N|!@Pih{E#~JtEsRmrv*1RAouGhRP&L_iHM@OUY z^_Vd&>EmRl;>Z!JfhoeE5Cq*yUrw}ltWWraAx6ZAHvTa-gh=S~{!}U@U0C$Y-vt{u zs`{2WRyg!Ed%a#LpjGTu(dEVE#V6&yWX$d*j8#zR)}49!X$~9!;D`W0C~{x10bmG3 zJSZN>Pil0N1P1{CA~2vo_&KXbmYlpr77E|MG?#z^0qt#f9k-vh4-Fq1Uvt5T{!*YE zl30)^sGJ1C@%heLO~v`3!S(UR@b!AldyoHhnuvpZAzlJV-XN?EqyVzfIf!(3HwZsZ z3Huz^%o)`C3Zj1!#lRJ#>*?8nBhjkEDghUDJ7Qpe;fFk7nYis+-`iV2zJs(#cOf!G zC0N`)*#OI&24?g_K{AeNXd5!{K6BtMKI`RbSCZ_xx6YTcQUeBzagrWk_l8;S3PD=BDl1r(W8qx@!N5lq-`9;UGVj;NwioNR*2>AVwXX3W%wq3al<*J>WLuUGgOxy0 zJBlycimOk(X%=;Lq16!xHJ&2;@_wrQ14kTKv&Crs1v>IU@C=1LueHb3ouQ<2qa4$d z)=Py);itasXH0)_7#LES3Ia3`A+*T8yn^O$dEJ-d`xHftd`wmh8IUjli133kXNXx& zM|*q6tJN2kuB;7}riDi5y^|ShO-X7mk>-{qBwAu5GiBa0hl87uC5zh8Y2xcQV*5Sn zPXg>kmb%@Yfk~qEUVG*K>1_@|)&Q+x_0k!JwHJ*Y{ z?O@X6qscdT@eeMi2dgKmJ$uYIPCQggp@27tp_5{`!tdGxw711%SyrC*cY7l6^LU8V z@$wiqcxI3!5_`5JzsG*@F*(j|ccv#(-U!vq0lOP&vkqk_3i10Lc!Rr@3wS@~LZnF@ zEJZ+0hr7qJNm(2r66$HOW{x5~VjuBYre8p*iE zQUHLj0@O)dc#Db&ZWs4I8Cy*=S%${hTl%CU$B6HAv2sBG*_5xWM}!s1bz_FXRD=%4 z2L`vlSO5CH+EZw$OA?!b$9a9d6M!+v__%B%^Iu9A2!L9ZHL=j|(kK81LOz(w! zmH-3aSA`}SQ1HPkOTzBXIBzRhj@sKER^HfGn;?Zs!GTnl(#MtY=)B*s07~D&M$mBwv7rg3g2ng_Yf=H8U zq!P|wGhGSIxj2mPZA$~$QnC52giuVaBMt}(KW+~cbLuck$LU%nTs&`7y)lO%Ko;j? z-1tjP7-U|qk4S_)cz*K|wq<7WWLzreq;nJy^)fLdY$)Guw<%%)*83E*S7w{lkgOMY z7-$e-Um~dn{2!6+bAci-ll-(VTqR1(dfd+^QRj~#h~qZ6(XI4@IG%${zDn#0;xfk= zQdOMVuIlDA2`>VTYuNltgX6@5?`<<+gchNT6#RvK{A3tXfuN89WI}Ikpw_kn=eE{% z7f+&btQnjBXN+f%hYHcIjbuzHL}qz8((kcu}mi z9@6O#hq32z_vC-`$9aPcGxf(@^Lss7sIs=UZb@fK)*lFFy+Rl=3n)tQ-z><<@wwYT zR@m-+#}Y)N<15l`MeVtT{!0yMI)+0xaqU6g^8nW&J%Y~uk9ldO$KC!!JguUeTRB7l z3-KM!g>n0vXu1(m7~r(^@g#95$+~9oQ^5khPEKuChk9Mik+*h#2ETjE@V&Ez><4S5 z0SwRiSZPuX+Y9aK6F1>jpToh#lGqyLMDrpyQYMf|tqbJq>+|hzy%wFYEoEM9=7rc9 znhzsK92qMiWo6nRYN&pT&g<>#s4zD_Mz28gIdyD0TY08<-V-&5s}c-p!L1xNm|c6m z&&q4Rw)gIO9p$AFOA(PG5~YCJ@6h`^JlPh&lNZMQ^F=4j=kn6H*ZdC4Se`~tVZCVY zY+*3UxD_%)FKqb(YQLb?!{t!Fr@p8;L%)+Ad>@5MhgaKzCl+C&7cL0oAo1-xBlAWp zsV|7aFD$POX;f`jk8Nv1rcfqoV&#WE7kG7*>SLgvuJI^4FWJPM_SM9a{n}Fxi^2U( zor!g*dd@_Fc5N?LC6k=hho72LTC1=2uh)~@Z4I6qh*!kaLxR)$jj@aoPN0^;ep*h= z)G+6)=XK=XMf|oKS2DLBuRheeOd}N6IcnTU8l5i71aWhoXX{L7^B_^4&KRI>1KQia>C5H8}go%&&$C3>$P_tH%;3Du@a^>-tqc}Kg9vk%A z%ZFHrpjD8eIemNR8vew1KBB*K3p^4Dn5pIyGTjdq*8N;CYdL@wVY*PW9Nr# zF{=sDPt0Xn6!9Pse0Aarz7k zOw!sDUZ?GhX_ah) zRlK6rY1oDKcKRDI({!ll%T#vU2lYGem!!WEt&4DwRC)3wDobYwJjI8|8`Nq#RFGn= z9X!_aWtqwLHq_FaH>B-=@QC0&gwUO;sQY6UA4H=G{Fb5Y?f&_5V5K4&G3 z;7=v}ZE5LN=)tn{xL6z8h>Sglq5?E6-8kaLAsuI>=XI4oBTbSol&Tu_1`tr44n$1? zX5S`_&3rTjKfSKMJR<`tYid@Srd5?*IgT)y$#e{16lcoPAA-g-mrVly&I zR2mb6co3%<&aG-hZU1l*b233)u;wgj;dfu`+g1*4G95fBWigXWFzcHhO-~P|Wbs+< zZ3cvUk_)t-^cX5BW`24sjihmpW$>r;^`5>)( zk?jWPAii$*)S|nZ;7w`6QOR^^az9yaaoG4K?Gy1mQpzb9gZEQEt*Zm zIlT?sGm&i-pOg^$jWvJser$Gpp$7M_XfshBLI^OR{Sh4>C&u0P<#J?1{;~98!|rkP zx`sQLcOyE`t*4yxh|lS2p{wb1`F{O;HJ;JJ;APd&+u^aQ&nYJD?0h5pL}=nP>Yfos zmF`$bgb*9E_HadKAp(a9r5N8R#ACLm-D`?nQdI6HTI091I=fzD*a)@Np6{rKAGgpd zk_meBW4!L&u05aK2%7Vn5`q19W+#5Uy;Rl+G0^UBl1#ybGu3_fFpq)X$2Ukgx;bBG z`CUgW9sf;X`Q4i~@8pF<34pQ(aUC=U@k@P)5|@jU!J%D#O9S}`5m$zh5Ssux!m4I# zo62V#t8540+I7a>y)Ayj`FMtZ0}l`LxAS@(v5(D@LwHf_Hp!)d9ocW*aTRyda4DO< z4eNJlT>e`xhPrd!E?v&w>R)+wNnBOzbl*r@Wm87pT2(t=nd}Zcj*gIn*)S%`s0HL( zGl`RbiKLE1Yv|@>{}K^c-66;<)ki#)xcNEt+{HDORgFdr2#Dr26=x7rJ1jegbyq#8)cxGL_s4OE& z5&XN)C+gCVMtYXl76hIc7uarvsUc=8-jEKxM>z|1k^I{|wJ79-0JYDD$sA@R^KbDW zn!}$=Vq!=x^9lJ!OSWQG9H1B0Y4_FgsiQUlbW=-b>z33m?6b)VKdN_01C*efDrn;v zONBo+5}BH3)Q@+SZb=m5e`DcUeYfS-E*YFep^iLiWE}K9e6Pq6txT_yBS7yHP!S9> z%o>b!m#u00E!sd1QtmCc_3d3b#hrQJeJL?(?D~kmr$rsv+7*TY@M|a6UjS2xn)J^Jne>AnOwjsrP#^N&U+(DJR#R4r!;*u=pAMoe zLpENX;8LDgz)}k6#n!`9Q7vIjk^^Gj&!}9g1&^nayc@d0iPY5w>i*GyJK5Gn-oBS8 z^o322ug{c6Sj5WWA-1uxHiprs@A8*!`_?>D$iB%h_DY~FV`#5DzF{GgRRVx1_=}K? zDUUzy?ECHW=QB+n@EMjoov+QOmYbN5TIH-EBLy-5pyoqfU4M;v7HPGLdmF0qlnltx zT$kKo4yM#&H}TT+(s_PbJl8=xIxTXpVJ9h=mugB&u*!i)s!HTxbL{^; zGu$rkM?kOlE=X837H0ZMva5N!%irlQ_coNFNzl;Gm9ljiKFXs2Os4pw_J@Enx$K(5 zu>NJyw@JI>+Ip`pKX0pkO7Nj#cd@68k;QI?Mb)Ru3K}6&a=kdSSG#1p%3HVcb@!*P zszyTIXKQD(tg4L&P;!+qc6JQh6%cMYC?VI{3A-Spqhh7o?SgVOztV zCOdMrTSp8~tk@h}h_N|K2*yH|a^=wNwr{BjtT)DaBj_#Uo)>4=W?4&UZ_XZI*7zqm zYmEZYgf5FA>BRMBjlXY*Gs<~!K%%4Xl@nA4cdVhx0FUJY6V(Ia& zD+zmfTehdf?0e>LaujTsqyBaT4e_Yza*oZ-=z+8zd|l*ENWu7=UOcinIQ<#60-3BF z{$S%q{6Zyqrm+XH_jgNANe%TsEot(Ik4S2aO54BZhmg41%lM0z zQiw>An9!FzkU}x!DkHrUmS>ugyTM7@LYQ7_`0*VL9|(EO7npa5?S6`ek>hSGo*T}) zVx%8_H7EV$lGjG~{9GZ6t-T?3arM zhZ^VFf!}XR$9_qoOvD{yGZ7{d{lw^CI2<5IcH!@ow;Uu%_YQ|k#fez#|I&lT4CX$k zgqvjl_}DbgcW=_3lgL_<8%G?Hzt-(n-2a~&k<1|Q-{gz#s|9xJ{hO!_hv%Pqa=m}ngQ980@y8I238O@kmv=c9(9R%)M_NL^S&thm&I$OBT=tepPnevA|IZ=*SNG{!t$|%+;F*8Lt zB(S3MKhehV{ULon-0u824r%q5YE{oD3gS(AK=gi$2{VXS!Pb1YDE6G0nFpKInYdA) zZ0@x4T!wpq-UbDk#6o3p<0G@bKLtBZNG1wDL_3xF@Qyh1DA^VW0cc0_1!@62?k+cS zM#E6rSA|SKlD(F93KE&C`qe`0i2m#8H%-QVFL)+N!E;~)Bpjel>j&RqX#6}d)fVH5 z%N|+*;^$tUbOP;oeCPO~q`^@CwP0qs?Y?@67ta-*KHeRw7=!*tdvgLIXUS$}bLH@I z{+J%{r^aHKi{kqBZ@<&taEs*LDNV$6rxZwea`9_*SNi_GZi*;9OX>&#V-;l33U}s8FQ&_jxba?+AA>J!3boYDF`{j+SId7oCT|9km%nL#j9Gpvy z=x?{mYkD&9DJTZ*vw<~`N;sddy)zr^&w7^Ln}aCuB#&L(_sv~78w_CYha9c8uMQ&R zzP6#I@FiI?2q@^On%!Xz?b18yy-6+T_EH>L71MsL(vn5oTt?t1c^Rd0rEXz;%Ohv; zPcK<*B>)CdBuK{TFb`%nzb0F?CWr^YcTGh;MtH8ibL(PXX`@Y-f`up{E!}*gLK|Tz z84lv9sZuplZjrG^F;$y^io)cuXhgd+cfh-EwP{ZAv)R%_4?PefWZ6qwgDx!gnL{ z+*awvvMjvG)ZI1pjdPYPHYq%^0Q3uGOMr!!oH$ITDL7$DBKbT4p+scCprDy?Jnqv= zb5`^dHQMQVr{zb-w}oacd0im#maguGYFrp=76bjs>D31LH@MN?9n^EJ-`^CO64?KW zZ*b(er^=u7m%G}$=0=v!9=$|ZAnYuTVv)mRHc&@WDmL@&3vlenN z!+o-ln+bW-eLn;xo*)pY-@U=NK;jOrW{47U%J_Y7$s+}HP+DQXQr~7sp{0mVX zZ~P%svc~;z7iS}%tJ(z+=K@e=Eoh66p>s#^jD18o=dH`dqTMYRPr z$jaCmU8Zid-^Kf}(CbvhmN}(W>#N$6QnWOS`T&VOp)+=y?8=IzG0qA5(RMr82HC{c zXdj1;KMAtTFM#MsWO-5-`L358+5*zQn)DM?_H-*IguK0~ar{~Lfn7S(PBuirVa}yE zfzlXfNk)(+wWYuSCg7-TWwEk8z%pt+`8A=X)Y4=xnC!QkkE&OLrQK)8Y!cEVg$u{W zBBFo_7k(5MjF}CBnLH<2o5D0Z9^qMBJ(w2BUx1484>>wl#jEoJqJKh$pp3Y3LOgu} zF0pkjLpZx8-Vx^HDAz%B1^}E&sprRP3NA`>flDEm4N{!b6kuw{f*~j|{jT5F8c7f- zD&Z9*KJkPe8c1C~Kju5YL(xz;-0|xAxwh2pVWuejD;7;a3TOwZshbm<;N$Wm5)g=$ zbf+&poK6}&`pO_{#d0RJw0%H0BYHP(uVTncDwrhpqY3T+B~Pz2S~JA$3W?pg-Bw_T ze!#`qtGV1GcvMuEUHmK-Pru4q(Q+lc;%_vimdNhxafl)R=atLY7y_&4y<F)mJ+KQsXZv z)Ed3#zp<3#4n#OKdb zR@K7GkJT&39~oRtxHK5k4C~LLh7P%J7!sGV#LH59v~4m^-E_Q~zB8RoQKS)}E#Qx} zRNii^Ad~shqEW9KS?mh1iY*miD+mX%k+juH@9GN}V#q@pZKvvA+Qm6a= zW67j?y%?aFw2~;de9mNrf|cxwDkuGzG_D2fM*LuA04 z1k)&=ou|E`%GVAHVV8`B$S{J9D)|@b;}aiC^m8VVNL19~sCCkB2qDDdcGe5i@1e^} z@r0Xb_nzCW)nV&liTj@zGZ8rJ%O3nH-%XJ*|B2b^T}ax`{c^H-=l3feKcpKs9YC** z>;~+;H>q9@m3^#RoTTMTE$(}HG;WI~35!56d$Awm)oc`3(b9VjHT(1R&w{g479u!k z!B;O~a>K(x5>NRPIbI2ILJWHdW=Hs?f@vyHh4Ofvsw_gdFR5&5INKS z7;a4dM{NKJ54@`jiT^+yfX9YpA{!QO*BLARBXC!`_ZpYEz*5pg34mtIy>TIz3uglX zDBN(w_O&ov1{eWBf+e9l33=q??*Je|oF6Hq>$D_me;P7cucgj51zwmCJo$SOnI^r& z(ELis5wSvwSa{n#m;IUEt8jG~e{r<(PS&DC5cx`p_!BARzCB(Gl;9T>Xf7Ax@id#% zDc!U5Go!>2Llh^#EeXLNWWjkE;6t?8!fbL@8R2sqI+H%_%u*RylN-T{PN+7cJlPEg zKrWO&BJ=BGKoMdM^DzLX8F% z^soQyrE@+qM~f(V_s@<{AD8Zs1t_JP)`mrW+9jWt+So}jY*kYb`As%KRE@NxZ}1H& zd(6`xqutgZOhKmnUV%9Q^wJ|fO&m>u6u}>N!qJ|2EM;oGQECqwMg)dT2B^+cQ5U&- zh@iT)QTJ=u2O?6Sg0R9thzI}<3pOkSM8vne7|w4H7y91?I#OmSWJv_sScq9Y_cs|a zU%N%2D0V}O>b>xBz^fjXDzQ$_?Q%QRUP0M4&~ISAH2?MA;NT>fc_Ar>{rJ2W8Q@z5 z!_EM?V;s+i()f{&&fIZ>6jHA@VgT4DBV&m1?!~%>fAjBL)*j&YHFbvR)>AYCg82`Z zJj?w<#|qpDRnt?-jv;lh#hL}|Ot6Fev#+5k*ZJ(B=;xnR z(f`8>;Ec0Fa7))R`N{r?*i*!~(zySD>>e9KYE{p#5ubDAAT;@S3VYx6jd0~+)P7Ls)?9#aB2XpW?@-t5i!G|PX6V6Z2Mf7^+w`vi0@8YD5j>y7|a z-0^m0pCh&s6ZoddGXD@ryw7ROLQ5=VVG??t*X_R~HKyljj5pZD(On_$e%#LRo(XWE++f_(f~=whUT>Ra@^)4m*(Ygmx( zp(r|E$abMQ++LW+!LJ0~D~Q{A%kTId3sTBilZZ;oahAS(-~wO^&y`k0mWp#|a!xIb z_$NJK>#CM%aDz3s8pY%No@YNhnWmA>ow#&$cdI4-!V-@Swi@B>GYxGjd0R2VV^AdQ zQ@bX^$MLHjZgVVQqE5aGqa!*~Su6dWY$N-|pVPdcby9(^q$UK#D~wl~FT>6KD5HnN z$|GKK=B=3{L1({_+6LXO(i{RJw+$6 zy>~F((RI!;Mfk9(Ed58f@~@9up|7WsD=USCb-y6Fi;Gi3nz@H$Vb^FM@pY~KfDyO7$zBcC7V)d6 zo(#YHGQZt<*Bfj`To^q2d4CkS?d~o>CNS<$Vtpn;m;d)sX~vqqzO}~O^3lJtpV3im zC7NQTYrkN`9tnzSq4Cs9{%Y}}a!|Wy6$DSS&}^Y-exiOYWTR2tHh_&TtjJ7N6l~+* zbL+S;*ItLLl-qrDPh%5r!Kc}j=_xPkLDDZ=N!Ou^E&GJuHx1V>?4fzTs43d*QJ}G; zl_ar~5^%G@_lWoz6gRK8yli0nnE&kIb*QHoRkgTd@AY(*@+san3ZCcT;#zN_8&F^ zxnPsFnE<16e5}HVOj$OaIVL3>apix~(EkEGWTOy1U+WIIvvxKO3|hSltWTU(w-@J? zxggpYDdNfelGAKdaeZc)ydNSqSm;i3(y97OX|%|f7tfsO;R~;z2GKKu3Z5eg6YowG zm2{*0nyx%vW-fz_`7;cl^&}~DCdsrpPdyqn(d{LeX!Ik9-79DHS6rmsFKA#(my}fm z4Cq;e(R;E@^>G*_+|s)xtz?iaDW=z}?rs;$>Kf~vS#!gm^Lx##2a=wR!Mj6te{H)(0%~6#&^X z=d)E-7ZX+axC*+2*38umY)WcTnzfn(Xvs9iy?+R|d6>#!Q8RpyZdbhq)F@Qs$9%*Z zH@&s8xGCvERly!dTa)8pXU3I%>WDF-+S+{4Fs+5va4s&L#HH`6hs*aaYZ_61%x|lF zgg9yX`qvXj_7kJ3G{?#yfZuGN9f$+q*E2-r6wub2zdc#wGJEIo(uNC22cBlvy;~71 zHf3D-4XrOBWpkT4w%uReL5S04xVa1;cE(nW%!fy8b`;N%yH3gNeJroWM+OdGQ zKkpHZ`$Org&L|I`$ywixF4$93Kq)9DJYLd|FVv9xn?KRfHqs@P+&T&9AVXNksHldC zb4Q03l=ogE}6yk$4XtZ`A(HI#!rKjG0Rz$a81nVT3=KLiHgMbY$Y6U;y(^L2=(A z0E8|IVB{vHo6xCe%8GcE$>isk1+)3EN+wbIjjR-#UA~hs1^_wFi4alSh#HMB)sF{; zzxT#5yf`{i$s#wuz4e^#=)6flbWri6S`IH%f=bDnJ^I#cwYO#1fQ^d@U30qQx`Q5Q zd_j&dw~XWciqQ0>*;-R1sFQV?9O)Z~+{gpSM5_ff|FSNo!qUyb|5HF3mw!MsOyq}6 zq!fzI8})bU*>0L11KeWd#%g73@3Q$T`~v)4b@d|-p_?PL30BTG79~?i0agWAe4J8I zzleH(Nc}M1tqU77c2V6Poo`=2Cckuu(>!~QUHf63h_SfDq?O9x&y?lmeH;64QD#36 zIdT|hWEif`>pvaL1dvW`-~z>NB^`S0zf^9^AuE9qjw|hXXgf#tX2;r8e4Y+yugl7| zdlfEcyY2${k0rl1(=jnRn(Pc}w%+WIs!q*w&%6o$npb&IR?_xlesMoM#<*)R&=QY} zxc0cVCX02O)^>kfda7JdUsb0Y^(D`fw|uT2j5Sg`s>9AwZg?-#C3NUAM?4WQENxuUc%xYEQ?PJ+_o)E>c+~OehyR+@vTNch=Wg`} z$JX$Hy0nEz{Vz@Q0Fls_dNaj()9xBq>n(FVMbo88Ay`&*qq%#7BKuWR2( z^Qt(L(#tfqwXMx;cNTi6?km*wdB^14FtU}!Ieg|YA&fMO+Jaf68K#b-E7BTdHMvdi zs>u`TP@v+kyaH%3o|bvgLXcc zA5!~M`=eD$6kkXRuKa{x()rKjpG>A8^zA*uuD@pD>#B_NTnX78Imh;-EC#63qlSFg zzT+G0tG;0#hmw`6bLkBDF4aJ>xpO)3=45V~n4M>NUK&U4ta-&O@IGk6ME#lF0qx*F zSlM8dAED_7YA3&`J@Yh&||6P>WA7_5rYkN#i)=*->Z5a z#`if9)X9lT!@i)A2e;@-wQ6GVJxe8{e|zEr4Sa}?0L0cckT8{#BND_et1#j1440&d zEPm>^dKG^05IpW*u!Ts1o6Y1nxa?xemYoT@0pmgdAwWwpGpK4c zog5I+hCUxm!I;ZR8MlT`0qw;2))AlmnYRZm4t-9eyUC{eIg`EqVI6Av{*_tXQ>VqKHdC?tL)(k5QeE~!$XvuUctbKL5 z2`3O03cp8irEh$)7qj46!Jugv8;`JW*FuAU3ktWOR$+3yJ%V<}%I`(5pBdgeTd}i{ z!rs6gYQHdUHrwL^kdUP2xGT$Z{r!arVvj=O56;0RWl)QJ^0iRU_Y(*knwr+P*$Ath0gtdO&OFbzz5KZ3+bO9Nf^lRyZGG#VIa{8j%p*{E1&&yJ zhpI-pL=P0p95Z2SO&g0Qx2tU$jFQh9Cz9XEm3@ywNJJa=? zfhWuT#vApw!#IB7m#>Q{0@CW(vB8CS-!j=z@zt=%({wQ{K2GtTd<78#s3u1=^v@EG z=L{9{2pFJ)j>P(-+jU)?=WAoS&ApM-{MwifBpH0^d^s=xv^Z<2mzcN=NuST%ab#Mk zv8&Gg153^EW8&aq)I;xm5|_>h(6M|K_y_Ozu)V1TXv|h)k+eyrD#W48m`rj-$H(uu zG}*_lmQ;NiZ7C(|ux#nNYT_MLVqeBsx=Y`w9|btaaZ-JSOja?;TDCWgD?ETEoV(mm z%%eQzqoMBs?$AF)lxwxMZt|d!`aOY7*~ivapPiaLmgUDQ6!D2CD3m>|rs{=fOQ7>R zrRRA=2>$2O-{&ud$0HnyEJ9;mk0%>;eO%D=gO5xU8AW=H2Cs=Tcf*SA?zc^fG+!7@ zKlF@j!%z#Z6v{Tx5E)%G&4n4z?){1jM!B-TNWTxc}r)oc{oGK(YRHeAuWm!{E1%Sa>zFCuM><;UM~r z@+}x1{yo*fRR|a&E6Jfwhr4riL;bd!jE{;O=_Dm+RC`rSMAYes-S&EE^~sLl)gJNU zlXQhtFc1jlIt0gIhk~N9+h+c)2F0dR!94^#Yv?+oo8Ta(7+YSIoOEK92bT7Wte}&x z(prtQiJNV^S|(JkPZ1_&9vewhH}nu?ATa2{GjYkE5|N48aNamg?&kCf&qr!RGoUUr z7zF4g#=0d+eSviw*Pj0SXaJA=v~}$g+rwD2q?a}7a#(j&$#;Z5qCcF}iI(8Ixu+lU z-VBCofn|uWIse(zxZ%53U$3U0Ns?W-HUNRi*pxN8pCDb5SE5%Z+NySSC)z~eZrzcL z0wNChVO+_P*U2NVnpi~i7_?Rq2qf^2-US9mx=a5YOrIz~fhf!e2%d{-b6&F;+$~OR zf3o>(a=h2-a$LseyKKX9dVm|N)03%N!pX45L}>lv=s@Fe_i|1HAYV9iT^wfdz%J4~ z^DwyQW^jB_Gv?mfbtIM{Y_4whw_RG(NXw%aCXKSRRc&!bErJ#v}b*8@saJJ?{L4Z`4A2_>yW-k({$k zIa?&M6|3+~9tFX}(N_M3hDwUc)`MDIm9`p9N2j(0A1)m_bUW_iFHN(??5%V12vd&N z=G+Xe`~OZP4BO9=s7QCTS?z=ud?~&X`!V8p5<5PGN0^)nNy^XPwJoTolIf}obcZ2I zjMjt6oYb!0y+oEqBS#dKYrud%;}@#86&qENfACuz0^J4K3v|yGK$}m9B>Q!EkVCG& ztnL|U;>ADDS;|UQ!z-0|3Oxl@bw~Of^=;cnHJrEoGa~1!6ejpf1*DZnhOF$F^+?WEJOX8aKa>M!5fCuK zOjMrJ`E<9lvn7HA^kof^a;PeBt;oRuwJ-x+{e@yRmDBtUpwED{*UOkj)z{nA6-Lc+ zq*IcE-}%D8fwnFVO$RYbRNG<_QCsBtEz-FU4WR6#WN zyEMZxt14+uNBdo)>*a261b!~vs5FYfl3vR^dBjYJc1C*;FinH<#3iy_D}vW9=z%!u<02lfIZ-}x~*!e`wG+r)7|`@PkUp|B#EXMjUGYHL>wxxQ_=d_$l|N``1=-8* z?&P23zlyEUH}Q__hrDHK;WXp`^XBd-uCBMa(Rt(D68GnDg;=GB0TdYO>;vF$@7-9W z989|)XqX+>-PpJg*McEJbf4io1BCzV2WT`~!;n^ChJ+&UF;x{2`pkQIA0$V1#BC%hLK1H-hh3 zgtx;;9g4evT!%wX7yigPf(R_r1VH#tL&#pJjpMuE%|4+wJHEB;}Aj)l7vo) z7rcs(e14>lZC}YL;1H6yq$S0F98qi5VQR zX5n|E;EgYAkz{HLO5~B@_Z&ZVD~k$wE;jlBz4gIapb(C=OM+BjEll3K5MH^wJlfS^ z_p$Y_hmziuOlae1n~Zdm!v38uyv#QCPEV%zv{1zmc2EHJc;PaC8LIO=n~X}TtsDD^fB`awec4#>qLueqBjlbV znw^BjBmLV!Cp~y+gXe)WF#$4EBr@{GXcua}b+beVX_fW7%-ge26^!ADVmh|Dpj zsk>eI_`i3j=6Yv4S65u5bij#t0%nEBGMBpPuwCE_oSbA0hs=n+y6)A$V43%y%d}x) z%;1R$z2ArPQv^w+<9GbN*YnSN z=`ebjGiRT@*IsMgabna|h&BmgWWv!oDdN^80PM>JWD#LoebSpSBP1tR9V5nkZA#ONv za4g8tSWBlvGeMtHEAL)oF-Ie#$iUqA1&{QL zaqYKxK{u<9Ke}&M-w~L<|NsBpS$-RluJNJi|9-RSP{h_`_P>wqmLOQu32arM#$(z+~Qg_>oYJia*zhYpeY(9kWX`=x(mFYK7E2CA|{qljKSV*noS+E zy72Gz2(GWIlU?8~nPZBP-ffzrjwpgc2|l|DDD&rbvsq6ap6?=jLyl`eKJVPn?!S7fb<|kl>>9O%M1~2e#+-6#m4 zYzWiEG8Efi3tjY&438vVI&e7A!>6ZJo`PS|OLS@t4v@#2;7}wHWj?1P%Sf#8Q~kGH zHpd&0Ht$!^Dl`^S{Bzv8ZgDcl=yN)!FsQFnl8omnzjSVT8h**h-cGgYGs{F4$I;8zkm%3vY&yhd3T-MFp$L|Byr9 z^U3Qaf63vA(xFDMQZ8BGk6{kK_#$|;&5*2|NIWRlG-QiREziJS_8UF<_>%Xx3;p}= z$D-;Q#PaCnM5p8Msflmh$|4-szfaHMP)?;zWz$*S=%cHuTFKLXNi<$xGC+ z=UPvaw)o>k&BMhl8jft9CU(z1M(bj3O+UPXOvO18FqU^Vq$1tWMBp7r219UYiThE- zU#a;aneC1AF4Cnu*s5u2db%W;l>ZcL zSBeM3)HT{a1ygpM^;J9Afun%)mU(TrO}`h8?78{W9ZAW(sA_9_|6UvV*n1CRn&Pvxxv+Uyp)El&A5HL;BEJAFO#xxCc5Ga;wAZi&z-%G zoxKw-)y=D?=s`|G`LfGi(hMrQ2_G9Z->0wrz01-ykC(ozv+GZc$KLA9RrbmL6)l#4 z-A?|7U--p7{pFixd^f&zNH1tabpXLDw=@8k*Xe4$SvQIP2KXX4GYeNn1OQr<3P=deARDssB9#fFr= z^O(1I#bUT%o3r2I0)?1nW{w~c8{$}9{Fl1+X{>-!rKJACgkResWrXj+~l53w*o zrA;fAOuj?^`!bV9toa)QhTgk?JC`-dyUxz{jiT|Q_iW|2o8z;EFQ#`(cSQGn&Q0~N z*w5fc`K~7~h8`d6E&sk1tec&BVrnldam%d`6#7jC8V!jgK9IIGeTsefkqJ3%EoyKu z%@BSo%a)&cSSxFIyiAky1MVr2a`RsM8;0?))76imCTFE}()2<(`!T=M%0Rh^C~-nl z+muk%Ms72n#Npx_y6R7&j$2}bb)jqtt-(Sn5rRU94w=3UsYqKdt3*l=zMj_!qUtby zlfv!Gtl>GwxP~)xwmfR??Cm>O@baVbQl@;tifCW4NjT{;5`o;7Ok0haK0&5+F%zof z7vhkP&heD3YHZJJ`0(Rb> zu-^Y7?Cl@ad&fbMz`60t|9ZKP^)nZ`(&mk*nDVr}LMlpu1-ErS?Q^$&BqrzK!NaOk z^Bw9WLXo6YSz#}8%=C0}es1gPnjB3ToLybt1FkF#1}BhyoS&WLv@Pfo;uRm8(Y3|) zB;Obc>3U+Q6z+Tyj3BkzD7> zhu#p(z1iHAj(5ev%DAX2B_yxIirnF8enWV0V>SI#y{6-Ky?slaKv>gNz-Ov@@OX+LP`)JBMLW1FET1jKN ze#p88VH`s-tr+_M%=l=&T)sxx4jByhKI!UJU_zyTY5%-@jtFtf+uzBL5PJ#P-D-P7 zOm6lJZbde;qv~s_uJ*GYVLC`tFA%{lUvg+}=977~%Dll%L$5$2)j?pol~`~WcXHx_ z>tC>U?v*^+x?#+YEnD@Vlg$5g1=jMrS$!kxG~#To$N$k-yKm+j*2T}@aLwAp&CYVh zKFH#mj)2rZJ~DTgjp#|Q4N1cp5-OM#cRO)=BCVErj3I(kF`INfXDB^4d@|Di&;tApS6 zWmOm)P2vBg_o2FXx32Tdy_e%lB77i4(nu@&AtN4W;c!ZL8PmD0CC4+bma?bN3#6SV z;z|}m_1V0v5ibF2vn{dIsq#G{blSp8YywGFS@+kA4xUCp@Ze};W%14@JldfjSd{e8 zYLwSQ4BzDq>0{AW^rvG}wcFYd30!zb$Fl4zn&rtX+{;$n&;K2BgXMW4&K}ZIed=I zoj@-9vH;_--~3hVFiN$NNyC?2W{K?_@-^)I#af)Qtv7X2b0AO8upi?#()YA}q|H6N zKlL36aX?Ms(JOWMq|l_Po7d-oS7Mtfp^SMJ3|MNqHkwJ0P`~ZV*VN;KFx^)VFn;g( z!RQ&2jbh)Prdl^iaJ{rI0;4JNp0*?WM;)Bwu_iYdF|4A)9c}gFi?CA9Gkj^`v(uAL zFzV|wmWIC!hGuCa_MCY<||;ojI<#p=bRDe)-4VZ!&A!q_O;Oc2D9n z@u`?dgg_?t9VyBdTIb~syh{sFVJq0K!`hI1gmI{KUSjrKkS1iW;%WRXazOnia>XP@ z%U7>xjBE=J%(*!D`R`pf1^2Mrtf*Kz=~C%WvAVANkF!_8#fU$2 zwYASWPU`%+>1VVHT?Hn$x2a4Y=Cgpl@;|EYfByVI%pr(9N-^?-oi}P<_kka=eKtVD zct6KQ$%G$J=~>UVo2Nk8ncYR)8{wN3_S^yjKb>u_4ZDYjhf6m9v#pPHuW0O|cEzkX zf0U(Mei>@=NqIb!+)$hT?=g`x?HP%cvc_@@p1hG@H~Z3AZ*_XZPB|F*aookoG*m7J z<{H?@-y*}9CeyZ_tWv2^xxh5uj;{wklvrcPbmAa-4IDwZDoDVGVHtD%caA|N{<)-l6e#4!Eo@qU$B*&fO znS^3wmrqC&xB9d=ZM9_Q?A(KFQ*dwFvvUl7UH$S<_v!3NJiD%|J?{ZM{p-<9tcXKv z)Az-tPHUNfie;9-pAh#hNL#~GR#+4O8hjw#N8g*`(VVl6(vIA}JqosZ94SQGgR?8f zC|`CcJD{CtwFCDRz1aF)d!)>&YR|3bL}1nO!H*6WA@$)UYQId$@CbLhc^X%b8d-%+ z*#{jvHO*HeNg6G@+m-ZZx)zkUo+1;8kCfy#iQS-1P9K=orwFchyVAWr?4Mh(jRQlF z@imn&5xuQuFb?Wp*4Vm$_7d7qj$lvYN z17E}ZwY81G*E4bY)StkI1q;mk5bTYt^YRj`Q==#0;=+-TkPsgqucoan0|HIyhtEjb@uP$TQo2`eb2d_BkpF z3D{Lk92}K3HK$>A6huU>STO|SwNPm0&W`y~jlPG(Rouv)r>AFw^M;U0zRC?2N}Alishp=%}*E6;&epl?nC&x7i$jnmvc$M>9Mh>1%8LbV$a8_ni?8c z4qViD6zCxwpGn88s5wPM;!Bu-uh_YLJpT2oq@x4;?=~%lP!q|8Wrgx9zJ1GTY~0dB4HeTkD|ECzU zb$+zn7ye^;d0}BeN# zycV@j*W1&uf-l1Vy&F2WM;do}n}Zpoq@>~#66ThdVQSi2wCF5CQ3-4bVDBg?p_P}H1K%uc-p#-O5FY0K{$L=i z{R0Ak4IpgWU{;LXk)*o@lM#^6eWyTvUDi zsyEwT2~6_Gt-v%xm+r+37|^7Tdo3G}oYeQK@aI}vSw#RL;P3Bm_vw>?n)vbW-v@t= z7f#n&<8obka?V3u9|NEED364wb-nup`?rRGTW1my66ELsk((9E8k24?P`^`X0{F+6RjMPu5*>~{_L&~r@a5hJN~C0-SP06*s4Rv*ifPhZnIVI-H z`#^c-7}?S@{jA9UoSfJ-H8p=ME^-M7$OA(X6&2;>=bv9(jI;*>B_CKXViFRAhNr7v z^hqOoyZPDKMHLlMz&hC5SCJxpphyC$U|2otSap*|RXXc8Etbfwb4H4!P8JxDJ~XQE z-oAZ{)}L$ov&N|1J!&1u>3zjg(Ru)d75X0>47$;jHntzOW~8Z!+h>*x=67~+9cG(0kL`S?sG7MP9Cs^j`}=LY0^m4E=@iA;7WZEZXmH#b#P)tQNt ziV6l#-Ul`etQ`|8s|TUQvat_&$kRwj{MNbc!Fa~g@2wYhEU`N(!*9+9Ikp9Us%w-3 zJDe_Em&0KU-?{^Cuf|#HfiN4|OA>!c%=a<=Q)2XwX_aaFPo5zSD7Tp8LW3JVTT?I3 zk3bYT!k`I+rh!0ECtl1-VX@2OUY%5w1isuK5fc$@hrXlO9*n^SrjJKVZ2J3mIN+l6 zIm+&T7j<#thJ0^UZGn6@KCXDy^XOa~P{T(T*?k@GbtV^VOh)_t;|;Y-*PNhq&>tp87*eOtz~NM~q5ahNYNjvGk2_JM z-+A-jI*lX^EY(i8ibE4l&*kt0p9;pTfF(5J6yPwn%@8ytcju1=l58fsVN?d%u7NT> zRk<`>+Nd&;y1w6`p($lk)mZzmosK>PGCw!h_iM3wreD;S7)`#^eofyuZubo^A+~qI zl%_Z+qgI?sN(yv1GQWM$v zRdm9p0KG5kS2L1bN4qi%X*)a7lt5yQ>OAiuWu^zBBR}qSGvNFHcm<98a66|fTd-e) z!QM;GuC0x4b_cWr=wqeX77I)KiL+8Ae-|j-UVLl-bqs7l9!}2fPeq&WlLfzEV2+2p zgo`JC2>k7E)tD5l^)fM>aP@jO$hhxe2pvTn5P#WQJ@0us5KQZRSaZ;L*?xCbvdwBL zc>Rj2Dlqf<+*^KnUMM{Cv4`v0N6eJM`-~0Do}oB4Hm2YwXtvc8gr+1Q$ZV~<%qH6- zdLxej)AaOh+U4F0waqQklBM#xdNkvQJHY!mVNm7cCKE*fw%S}K<`4y3w?d|{vQ z`jF=ncOd;;EgC)YpiA{!^rKyMZavqDxa~#+0z||9_V%{02IVrPr-RX6f$t4$ zY_Q=LHf|_LQSk{)Z`F***4URBW3L8{6HQmrs+s3%|8LiIhISFQEMB_8=JOgSRlb-* z>-HIwyWhI6;XTa0+|#=dvDdcV1ju#{SUKlplY;hz84PJ-d9D3yv5Xm#6$?BcZ18g8 z2W*WwI{ewA9xZ%ahwKVG-esk^QH#fAW~fLBfrK(>jH!CB1$}r;!GUa&+o@yhuajG@ zU9ZBS4U&{H%59j&fZi9NV84RNK#UUP24 z+k`($OXk@5o{{l=^9&ijh_(6bkcTK*Df@Je%~Gen^T-w1{RU=cr3aEF>ipr%2wnxd zJY(#3W_#d$TZ+5)lRWkG#V4Kkv6AB4xF0(;E!P(wfH0|N^n4}7# zpKM1h2rziU?ho(S;`V#h+NX%pu7X^9R_X|6xAw_`0#tf><#tS8|C+`OS$u_zkBHrH z0r&$bdGSd}d)N1B+8N6x0SQhpC>M|^0g(h~8UZvObh~jRM5Uvpl{{wUyPqNlH1ixG z_Y)BV<~@IFUEaq?w*hUil=@9gB2 zaf0r9|690F#$3R=vPgf>a~QmYFjQGab0IzEMR6~W%PyET;I{9{NS}Q!z3@%8eEyQp zLEXGc%&G`lU=nuAyW7WGV&!P-ctWf#a8IlI!8>S2>3*by%v@ZF@BK{15N&>5JsbR_ zye2}SEffFe`*c>`Vl(#a2CA@$XRuO?qrJqa=Z-eYXR3T<3lgL`3uv*v{m}*&J{!pu zf18>i>!tFab?;0&yc}g3e}kDo2YU%Hige0iWd%eFN=AK_lJx_r@&rhDM{9k4NJ3mb zJrVg1Z#^=($V0VAN=s2`-k1CyiJMgyFgkcCq~Eh>f{QWFw%zF_YCq^@jxDPAO>}8` z*HCla(05D{eUFAzDT+geEE= zFZhVUK%5pWMw*g@L1@-bVP7X(hmwqLX={f~{Ybu34FIpkU6O(yIh5c2mGbFkWNceo zGobMQNsUP%GWt)9c{y}>iGo7;|A{mBx<(QX4y-ZKVW3=ow}(IWDg`2AR|gZTK<_~L zdN*?ED{}Vx4Y=ZxG31KzUt0;FA0STJucw7qp-!Y*uP?ViWE8&qspRG5{nu~Swe6w; zawSUiKp;TO_qL(b{i%C=Be1v>t|6rGay#>SRvrIC%W{ajg`K^eHErwk{%pv zxK5Ba1p2vIx#W9Z!_A6g`sA%DmPT(+3(t((jO>kC#m>|7Wla-+t99AbBz|TZ9)f7! zaPecNAlDds9Oe>8Z~ED~<-nyyN&ck0on-&!XJrt7e(#gEn&W#EM!xlkRd?T?#l)c% zNf_S9m0iHlZGo5)DxsM5(R6+~mw!qr%Evo@q(onm@_wrQfS<4=NN)Qk;4ni$fF5Pj zZ>#^{&~L~I>v}G2Bw(6fm#wcDm*-KMRZCV}Ed=dS&?uBu9C^rRm@JmoFGkEBR; zp{2ki6BF4Gu12J;qGzw?G_N3=fn}LbZdm2tQ%Z2;)7PeJ?1$!Ule1grwkem*U^ zJQJvj{!I_4DXFcU60Pa|w8--C_-Nc}$&vwRZkEGb*{%XWXLJO1sM`SwvytCH#$BLT zM`tI{!2&)1_VvBZ#ZHM}W}B<;863xeiz{NipsN*0^&G^nU; zK#v}2UISa1H8sa9(#I^eD_`}+$g$rz5-b@s8H(@Gk+?t$7?+viuen%H$-1P!I)e4F zl1xLM{{nc`eT3Xc4Zslqhw=|c0PU^wm!_GZGYMZum0f=B}wGd!`A?R78yR+fTy?pO5m) zCuY%`#XCp4Br%*&3=OphP7@bvPU16sB3KbhvVV(nOl zR#u2MaB-axJ_8IEfQ|s>YCA*M#5^!C4=^IA^ja?NUHAWPW<)g z_VIpgxyPH}<$hdfi>Uf&SN)|)o^++t7Apl&x{`iEMk^M(H<=rtC!7F%qO><*3giQdL{3yai?o z_EnuEf9{`GG*Xqz1s2y7wY~VH1_o;MJ2TCg1}{dD2TDs5I{=m}R{Cxgjl*pJM+~EC z{%&WIxXKc*pzBZU*owK;HCYIiowLc$oiPRFiQ%UF`O-$BfScd6Dax1QiTR1c*hG}% z2fn(5F8Lxc=0t&yjU#qWPP9VY{S&=+h+|qLq(6V3+d2k@xr$8pg}=**XKe7uZ~a!E zR4DoiziM=`eXNIG%ruCru%=qbU`b>JN{ct2-GBuw!GWi(8c@U9QK?4{u~LraTMjYh zP^lY2rCDS|Qlydl0D5uE3S)PSF6T4{<^T*TMl05mQN_%FKjqk74KK<)&CLYlU_c-q z{EPW$L$K?zHa1++ebd4!5w<-4PE4>HC zQtG^$9HDJVOWK|UHP)7hE8Ik8;G^)C#G>cJJESkeyF?W{cDC*xe*3*BPy=5>OA-fI zcS@VntXM!SY34!%1EYdHp0|HEIeDc;a_9(DIf-Ljw`Zw=la0WU*hHC;cwsf>$$ZL* z6v9#VFi&rvH2I2kPhjNpfOEoRawQTUb6ln%%G#}|JhHEty8luj-`My=@Mm8deJ$ta zOFd`;`^-f{b>)bCZmguIkm4{W5ef0mqH*V8nH=iQkMjQr_>&cFr^nM=79^2}OVm45 ze41VdQL;F8s+hfdZ#qirw6$f2hmAOny4Y-95BxIZ%ZW$}iz@4}7NF!w!$#z2*YI{7 z(|zAI`zOi2ilZIX_)p(D4NJ2jNV-Y-|NV=_$t@&Q8l)mib9R18-$nl)GQj-gkn-1- zl|@`#xo5KJ@1D6|L93dYxIc~#3fKN^aLRo=?=1xSntxOW#>e&Vqoc?Gi(CN&Ge9TS z^*nr%m6KcT@Txu$xedh%gp*NU#|{Sg`k*XjhN`NnVt_Bq%*<3$y3Q59_sLN#Cn6^* zS~{XFfyir@*VSWX@~4oH6=^Ce%F4Chrqq|n(so_!#<3YL4esyL;o_zNNs@(iT!BF| zT~|kA6DC!-Jn-KM^QTNU>Qwm#&l!qgmtJyJXBi6^f3;4ng|9DB(sJC(!-;fVcP7{f z;rjB@O{dmG#+OE!32zr*UF%%=K(zTPmSBc|Z1^K4h1FtTIQqIfT1SP3*H8AFj0|@< zQ}K1TclE7Mcc7}66CnYdBEaL2sZznsJTVS@+>-S5{X**86*H;C;qJf45(tTM0*_jb zwEYfwCVqEAX$Is*Kq_dH&|5gh1G+U^--qMMs+tA)!8yJB8nc3M7M(d2V zd_{hkdSGN)1H)SzOZ$_RwXpI-nhvKJpmywd6#+UMpy`W?sU?fCUp^OBjWO$75J>qKwuxgEZ@yEx=t*u^KIy$oeLF(c{ zN%=4vO+-mKGbHi*zXeY~Mi&wiS_2qmD*!v!7__1?(*q1RkfbB92Q9epWQx1G@{}3& z>OM&oV`pUG`*w>lFy{B9m`2imFAY%R!v`&{pXdSz`hJ3gCul|#pjNcC?Hn9zfd(SG zB8J@wAqnDp8;p+{0JYqAh-hNoEI^2Vt%1-HBmheyR+uDB^N*~_0fXH)zMY($2yVaz zuEL`Q8r^7hT{e|fRk7rfdZw-pSpQ9(lao`KhQhC3zp83$4@OgHYxJ9u1~}cP-}YbX zNYgmkzQIcxG4{Cz7S`C99N?1wA!PdnhWTT>KC>@Y++#pTv~DQeztg~KTfclVoUg5o zU%#wG^ImSjv&#ev;8*1t=Cs%=aa!De^5%4F!cL&Q9xL#jf~`yD6F5O z{xV5Q;D&7heg~9i@H$=twt()}I(fSOnUEKcTg7?+k^wV|x=ueHSqD~OMWy!E<}^bfw_NWlPz0QEL8=|TNLSbtguh{VU7WuI{cDr0FWz|eYkCBkzD@p-y&St*Z0S}M&_2o*J%(11I`;&{* znNFQYi+C~;@8K6(u>0GEf`oDpl2i*18JCTuM$eBr-oEp7zIPXc&G*OTy4akLBAX;J z`UKQTFKV$kIJ8@CiS_jQ^MTE`(U97&|JVoje8Q(TmXja*RrlfSek5eSzV`x=*Z7}= z(z9s-FDT_|?8M$36l5y`1`=VTBbp4;Xx`pug!HftkVyiaO%wX>-ES{r`Bz%PKs%lEv$?rr%8VCGZ1fl5gY!c@)JAL)rH$SAq_^I_e zZ7?c@h1Kx^n;5OePUq9J(Y-%&dk3ujq5T#OHFRW;I!Cd%3ex=F+BYnEA92IY&xI=G zbAQ&mt=q44Ix148$&CmE@!d=i_quSuLc;P(r+C@v5T{A-{P$556(s@rKv9vx(P=Nn z%V7?*kkUuSFL)~-hV8t8dQbylnF92Kh1D^POMw8OFYokbH_p%hP>%|W2ymfn?>uqG zWQ;ALLL>XeYdkW&A(r3EGJ%a+D7ewrTW6#oug%pI{m@ zO>N}~$*HZ7>=VQ0nF$nESOt^XDxp8=^tQf4{yM*S+)T4|hRcGjRXi-|qw~sl-JYuq z)7)y;4>n<}-q^w+&vWHEe@ztsHrRj}a2Ul3?TLsgLW`O7R@tET7NkfO=2VWk04?b8 zFo@y^vk45WVR~PIZ3afi!0f=_FP!E@7imZ07UJI3VGtKY{tjguVbJ}qRek|ia^iv1ON^V5U#dIZaN9m(6qG>&m7&cYViI`AN2?*#_h0hz z)P&K9%@T3BWWm&`8Y&|+%xAm?5v{!~D)PKG@789=Pntfft|5iNgMU)ePr9;l&Nf4n z$wM2tg7$q_MGOh*+~#KRAA`W=MSX$ zsXi$j*-z;f&tHp-r;6I;g+JuANvDMGc#vPrBw^WJl5Bqb=xdlcv@;O>Pm~7~;w;nP z93cAwP{Q}|_qY3r-X|phl5ZFT{%XL?4W#vdN^)ak3XqHd4PyGo54@1)<&fXIXJ==J z@5Jxk=$Z!c<>%+u=rx7`GG)g6{JfGwxNzZWcXhRcM3y!QqD02bCxLeI_zc7MCK{!? zrrtHb9>J_EhyN@Zu5MSZ*{!Wt*0cl% z*6s-3xYmg$J}$sceS`*3`RnVG|3(9VD}eg01PU0ruy>ofJiwIuowXkUb>H^(Y~$&t zoOnMc`t65VEfyI+KS3`quSAI0JKznFKs^2--q+__+@ujdKp!FWtX&E7arF*eqv_Si zK&)<9oq4LWnC6jf&(PR`ZcgWiPnhln>l7H>drK5tzk%s%2vy&}8(2GAo3u>kbR?eKZ!x9)r)=X}2UfCS+Ew-SwiXs)fISBQyFNf? zl_HNYz`2g*MoLNwsJ{P%U^S+Q5~jQ7V8HhdG>+ZH2Y_m1UFqNOGp**2NfYtFRUm7)X9Bfbr zsg=@W0GFiVtC{FeWwd$;;+K1B14SN4*wCJoY6wK#1If$%xXkm1j=PSSHdFR|qZjdi zUS8zOOUtxQaUC7jMx9nD`1p&CH4uxKx>8mONBcG?jwUk1;Zr-b?#&bwJ62Ij3YYS0 zoouTU9?H}uUP(~DgkrE`tG^W%Yvk_vZ1!Z#SRVb5 zg->DCl)y)opbcM!GB5Jo8d|ccRM1HmbIE?Hz8+5bJiQoyochdsT3qIf+f8M_ZRR>YkKh4CJdk z%)*{Vp`r{yTfNca#w5I)x;%dnF`ODdP$J3N)(4XyaECAp9>yvVnqX0Yb?)AMMP;dZ!9N06R zSvyGZZ=jB={eawNbGi*DcGH;jM$Oc8rc(UbURGB2!|{gSwTZ9))wZ)m$O~aacf&Q7 zcpszqo%&0!=8W4bBv&Dacq#K^qstvalyy@hgg6PplL8(-x#Z#$?Oy^fk`bR2F{mX1 zMj3b{i@gX>aW|fX7W#u19Fvvaa9DkNMomh8<^5>3zOwy}wwGi9+{VHH0xt)(D%=A1 zsAAlbE64JZQxOlhyi-w_W{O0aQ)5#Nrg`D+=n9hqiwj*EsL@`5UEJZ78iY?dT>4Yl z#^R1QKPuJWp(2{;Z}ofn670rtQp zN*P>zi6$vz-?rZ6=+ME1Dm7945JiLZD@|Sj0~jQ5oMzrZBgLEf!_0tS`J*G9wao>y zNvbyzH9dt4qeD*ieAzl_$vR<)I9H!x0G2B(T1;B)dXAEm-n@+YFGAH-7I$8@Cj)Eo ztdY*hSPX@hOz|Wx(D*XlrYCdrD3?`MT||9F(V7)oRLv0|bg1v1t~?L=oMxH7B7jNY z^`e-l?5TFZI#BIz$5KT<$Z~;wUXi0La|xl~m7#T}`x%pFc$Wm^`Cu>s4esGQ3f@t)8r6=OWeGE z8?03#T~tvp%rB(-h0)D3*GMC&`hB)zkVxo72hDMogA#h<3w|O`D4iU~CC+MTD9tOU zL7V8Bm_NbeBk*G3AXk>?L1i*}^{X7_WTjK-FjB%*W#r{WRj$|v|_$SYIMxa z&gmZoF{smqe&8)xMNkUUek!pQ{~L-L2u^Z0MQL<|AK+)6dwbUv6qX|cSf!z(=Die{uGxNG2dS*6U$@_tW-|85-;tE%nHmUVUu|-{$jwpCy(;9X za<8&U-tq$MC~M%G$TuqWyDjh`YIbb;{aQkbPDMDqXVyZv1Ev1!GKpbSnO+ZSv$#d; zq%`JbTuy0Yp79w67|InTWm9Z#H@b}ihP30$Y4xCybw3e2DRFYG_+?@kvlqy|dkQ7W zdE`xIB1{0=tt`ThK?PJ4)^b3W^2G)h-TQB|&D1Au?@S!_FKP%2IRYkA;c{~gWkCKX zW_^pSW?)f|iWr`F1k4NNur#$|HaQpQlobtpZ@m0RIT>&zzrQr=)C?~t)1k%DV zH=E-^KQBguV?$m`W-S#6Eh0n7Hq4$DL`tnJS8l#0zq6eJl?D^U;xSc3@;cF}_%nNi z#3nS*?O*HRlqFc~Y`6YrfHRe=t4unODnB9HiXV}2-PPnCdJCHHvsKU{ z{b6LXBZkjw_}M<(sdQ!fqq*P#vMZUJGTMY|0o2-@6&?+DLC5{v(Gli;&ycdBrx%+h zhZ8P;BlrZhH)pk}o1QNobHCE7h%5)oPYh<_D81K%##kF>$Ho*B=~W;? z2YHp1VXk&uY-~gQlO!rR&_NA4*(GL8CLARJU$!An`$qScwW!E)eJVG%75Ru{y$-2r zCxexF;5;@*JVOCqB}81@1>QzC#t|EqB*>ONR1s>Lt z_j$VAINAQtDjAK)%pj`SkXspVsH!=i&;5CHc(~vD7}^*y5K=bCugr#^@wp0&%*=`L zR@;pq6zh6tO~SxE!f38gW-pc48@qc-cKx?6b0sQr@>)+w1j>d+9m(pm!0DmB>+UqI zqZTnBeib-5M!$u1W%+g`J8I!fd0;RdwZgn)1)kcYAWK7Fiwzg;ttL@+MX0^7XO2GY zG7{M;QPj@pG87v%TSEp_?l6jTUE+gjqLX2ERH!|8G|+UEBs(*WpTnUtDLZ@qKc@Q| z7S2dd1_4L52~Q&;k;yjapUdQvO1{wZ4Y)P*qa=Mw(NOG7z7ynMVGr}4;5)Jw50t8I zGEy@1MPhr1Q0@LbjkviCs6u&rA_6Jb*Xl{UpvTtgiT3Hf=Z;W#mDAx1={q)Ei;xC{ zEy!YoPF+P$7gFz;rM1l4#bS5J(?Q}h=!1$I&{W1UkvZf{T=hlScra}*~^_58Ew0MA3d|8skLostw7VOtoK2~<<}^X^-Bpv~^Bbbcu?&bt3)Ll~i&%kf*d zj}n6l4^_j^FfN4;FxfHWC<~@p?6SarD!$^*@sE zJFk!O%!mx|EgKEUiCw*?2>PQAJZjnYd;{C!@xqh5*iTc%L5;+gbbbE&&sRABb%Ft= zA#`-~A_#=EtEjFH69(1q_UD5G`1gNqJvF95fT+Hov=tiw>icF6`TBSUYyOJwe0vX7 zh?-|ZX9BOQj0bL|F0ZD(F^D~(rq;mi@XK9El>7e{Vgwr<>3%+|C&CE^h<3ne=5E*f z9+VlRxO4XZi70RGuCjV38O84v3Ifgq`CT?AuV=((fGe%9)SJ{VcMH8U11Keco4Ne{ z7V9Fn_dZwTcHQHA9ij3oi{yJ`!@fOrV~%mHK=6;bZwkJM&kmnfEUX0t7p0sUQseW> zsr8=g+P573zb9065dq7$N&T&;u6X@2;PTx8SfQ@JONRekfB$T&UMD&L;sK0<09jak zB1Dn&4h97*Qf7aDEh19`hXE4L`+O5*ZGAv5Jlb7^qM}JgX{ntMbE?!LE4`Oo+y1k9Z)>Jeo%!|tjK2n*`iRP zdJS3lR?D%>BLDt_Q!;-Ar$l3;yQ-nU*_1|OGBi{HsY-99Uy_NRz~0f^0&PdE-1W*( zFPo_Swqj55gEBU8(|oc1ZrN0bTw?>5x)ye|ow!-zOGYyiugS_}9*!>qeCq&<8ROw+*r2GtJyy_D5X z9}dl!@F!$9-CUTE@T4OQ>@|&UkLv50Qu;;RoaF~s7 zpw|VmmNpGujGANCbXQ*9E4@aex>k&uQ@W8cJ__WpY{C7W7bL)qh{vicJ1wZ9E`ciN z3}#T{YbXyb5qgsNvQF)Ym{eh=5Sj=tDY1Q_iNC)V_L&TXX&Sk(e{Bs(eA2@3M1Usy zdLkNY&hmxUG0DH_C}}&WhDbNkb1%!~6>VUOp*TN(KklW`txGRjRmo4mbh>YWvU{}U z-<%l5z1{b%S-~_-Hxm9&z)K@isv2AhTE_P@+*ZHdInNuo2jj`_1m6Bc)z7nI21jX{ zE})ziv8X)rNtrRjgWT5IG3=n$zS4_@$2t0%oct3U z<|NF^Bw1vL3a%(ZLzGd(bDzuEWW+@FGhCIz5*-{IM9?*ILxey&e17nK{8(xaCe0S= z4VaQcfoB2;Y~U$jIYq#!W)9eAJu*EMw5wlRZ`*!&T)r^iM&bTh%(P;UOO}&TXBUA` zaq+8I2jK>&j%2HpGyiqShRV$D^;I+L&CB9rE9g&uF6cyhiy~17c5xsgCK zq1z?WVS-_tbSwJP8PXYAAua}~(z^RB$GC`sLYGne67@p#&Nvk5f8%m025D3^1*%K~^_U%Z^;nI0;Z2&$ZB#7?=C zF=8lVl->6||Ljpa0$H(}eA5ht6Qoo+P2B@}+m+~V#8ci$`h}4ZbmKO>c z^^J&`IUm3y$jJ+(cD)vOrlM|$h)``sEJX0tju}f2x?Lz8t+PD(YC42>Gb>MrgRBZi z?I^YgLeoyEx?(|oBc^S`n=(+!59bQhJedi+Dy_N;CW+Y4FYgS z<|tjU@iqo^m0)nuaw2K;pr_6Z2Yg@4sh-E;o5dTCw4|!+CHCdx=HHP>zoc&N-j4oa zh(c8F?a$f@Vc${vNlrVN=K$aP^0!{l;}9L7z8trD@&QwUJs7ZR3J+?2IEwl^Zqp1n zur2++(vME*f04EJdm`DVrF} zlb7j!wr*VC0UYQUSrbC6;{hX4Tu1V$zs}50f(89t!P4N24z{S-#b7w3yM#xywefMRFqg(AH z9l*O2MSb5Ji_P%9H~OrRt?63K)EhKg;0R}h5>??*hs!mA^A#c)G#ZBuUkKskv~HK& zC>{M-5vdxvhP}V%#SLzNQCvtc*gm2dy}vX&G?q#jZbn}He>}YhJl1{t|1Xgh$zBl& zA!KCF60&7xC3|GgtOm(mAu>XC)=4<+y(2q&Z`phPkMp{}zyI}kxE}Y7>vW!<@%|jg z>v+AMZ!-7Dcg@bv?iwM4Zct8=(L`HLW_~ohANdPQ-&@utox%kFYtP@+A<3AIQi)iM zm)!pRCU{@QSgRBbq+eT5DRePz#C`2~dD796MsK5xj=;EwGd}JWqs5RjT0z}d_AR@k zgZI747x!U_7*l@jTtYHy>zW_Dx+*O^LV`_FJ$TI5ct0f#eXDsv5%lc@^W)^LN~0pl zV_B1bRnnCD5$$7fF}DvH(uA<5&O=G-So?%c8H|tK8;u4>#_-WPR@O+gQ<23HK^+mK z9MiGJFUqy6RXaZsrj~Z;Wmd;;}vsHN`{7tyy`?wkw!! zNQarkbNOc2}pfkC#X`GZSxj76j*~T2_T?=roc<{5I2>oM}STr_0_{ z9{&(f{imCfESJ5HWaoQ)8FfMNk4SRai1txui?D1DHv^&viV>Zo=s^~7TQcz z$A84Sg?Kam_@%Dyn_7YZh(^KSjIIk&v0hLGE^#pQLr$cN?xK(Ge;ElHK}SD`A{BX` zxrWh-b{_s4A4NJX58((fQqZp4r@-am=6&ZU!LKlwYc&^Wlxsqql52127-dbRHQLB_ z)k}29_+UBxi|mQBPV9v9a>^HTwPjBk3l<`exMy*0Vvb_JJMNKJlVjFNUn9t2oL+Ry z_#ncwZrbKb#M6)GfUO$R>-r&%i^mrK5Hou6tZxXbw8XGP@cCT}=W3`JRA*NKOCwyM2Hd@~OImb*x zqetqOEYr@T^e0#WBX=%Ua8I*~@WU#Loo$QetpgDvEb{Vs=;w;)T?lA1Sc0U4lFY2E zfI{~i($GOVYH#T`1ZImo_RPqqHz%uL)SCvDD+tb1O&xIf z>_(@6{NMYgeDWDXL%c}{m-za=vB?82b>9WlD^UD+R%pQdJU82Em< zv#-?I#}4KH=2gdvr=pV{C=C*o!(>%?kIVi@HfrJR^wEizYyG6n@BvPI*J1=?ApEVt z!7o)jFMGITzde4FmX{ZNdiu8ZxP{a4a6<=%2{0pZ68(kBmZ`J0oJ}2x{V%mO;nXV% z9RhR^E9>jVla($fEJu$ZB}Re^-T(oga{-jcRKMv9JQWB(jN4X)Lf}t?b~!rbd_g0C z@r7iO)juIW!k?Sf!=az3e_G!Cv2uJrl<&=YHm=O=SW9a3_NX^13>#TBM9~v>K7{0# zXqgb6d~`O~$dRpRWj~z0YhsT1AY!kTL#g}$LO+@H<9&|#_LOa8z-Uj9a%Y}kOm&0b zeU7*X*Q|UVWVgrdb$a~zlD=jy@j&17_1Wq5h=s1L`#O$Y#3%P_)=EVUWAED!NrlmW zmd6^^3=pWIi@Yl13TGdGQ*=*_0Z)#)F0wnL6sMy!+~}3~3e%!nOs9%=t@Qo7JQlq~^F<6FoS0TbZQXM- zzqi5tMiF7MmsAq%v~kn#x1pntA%pO&jFi zvaE33|8>I6}Ps?FJ4cpS!E;8`!(gaEo?V zKc981$zYh+{K`3#(=~=QTh`#%W|!=i@mh?7W}YL7@tdJO?SiAYgru+FUW9Y}ux3M5 zqy9Gratp_WxwmYX+>H(M78;4g2F+~*z3j6D98{s)qM{H}FoH`Oru1Ol0AmI?OW=P* zM@K_g5i)b&Ot#>yw2>+=DJfYgpP!w^iNDd}a0BKf6UTN^*zg-*M%>&iEh!lY&9&7b z0~ZhnTVi@2ixf5}4zaC4yQf0Bq>)NgKf*#8teTFjgIz~zj}xRp=Kt?u&kGurW)J1d$#@>~zoj{M?)5&+@L*hZoGOka={?_NN3g3B-`s5o zcmBQ}`%R`Z;A`=dMV9KFCHgY`>7_Z2&zG~GY#nNydWbMfmK~(>Y2G@8tQQ)FTE@p& z#@$jjepz;ql`h4Gp%Yv2hj_Wg7x^x&^QW$1lcHOG3t~g<=Hx3Yt0?U^-hJg!>H^+| znVckfi8RTPr12vLRNv1A?|a^!k9p*RCl{dWT9}!^r?&9w&=H$Tl`u2A8LyI9O6C)> zOshbtFOhW24u%Z!tl;5E_F&xlth2k0mq!zabgcd$p85SupL2uPLS;o>$4)aPRqXMNvTzd!NfG z(b*xv-7X^j$+6CaLl$yQ0~2Za;4bpAo#&4@bup{ypW6^~24u1JI62sJ*;#SKcyp5O zUv)ncD$(!Jrlz3z@ypbonjuS%HUKL>O7=*rcE9n*M0+ZU%&=A;# zf|8SI!9esGJnD$&n<%H7%lo>ZAEC2NY@|BfdwUlhsxRDIdi$*1_qwF-jEzP}*1LfxLq!YzeEm|H*}$JZIVxa0mcQ7N`X)S7ay9n!w>3O}%)am?2c(PsTd z+?48r-=EuI;Vo3h_RD*u*roBC;>CbpnpWnX&HfD)^>}r&&+i*(5bU#ic?*b7|1=D7 z39)6!C+(sz1n((LOAv(1NbZKJk>-?%?r=tBmV`#j?_e~@;#U@MW>2%rnQy0kO4a>2 zy+KbA_F5-yN2xj5FO9E&`J`n~L)GMFWSTEmK$1&>)a{;5vU10U)_b~ddaEoyHk-8O^dqMi!SfcGL%mhr>Lx~a4C7vQUbxoO9J z!X@k2Rf3DcWTZeJxI-vEuISN36CP*v%#^jT$1Woz(V?%I6#e*u#2+H?Jt^YK5Qzjo zIF0DrFX2A>=_2RbP4Hh17aI0LTQgc>-WxTng(={DO44V6AI11cR8-F8&1Y~HJZpQl zN-Yr1CDo2_lT7+m)T2M)wix-_M>vE1$#X4Dy6&UeE-4D1RF7%Ph$*@Z1xthSAhI}wm$V@ycPK*%Xl<$=0s?c~hS8ARhCEsP391H1qN z?T`yWg+(BK4khD2mU0O=y-1{u05brYSBEBielA$Yd8u4#l3>wBe~$(MYo5Z|Q`Yin zpR;~kf$%nY)}+Suov4n?qMv9$U;+Wt<;SOjO|b+jO)aI5Y*0?9zlz74RC_}c`_^dw zOC7wv!hrKb_UrE<-}>f>Dv83t0w^?X^E)Jymb!I5qEK3N%7pWe78_$pVILVJ!sEbo z{7nIuA@{Yo=NT3Techu42I_^9*2ZiRrZBZXJrzQXS!xzqNn-~mW~N&%ZLHzZTUhMQ z9z1xO_pjAZPTEo?;(#cqeQ?+`nVJi;w6 zb=xSD6hj=Na>;{s_lpneLZR(+0#*s1Om$4p>_bu}RM!o<&;#My&r zYX=)2atdgi>E$@#-$}CG$eubG7_)iYRb^X4pcA{gDj1Oa*QYhH>5tm1>DL>E$7_n1 z#AUoGxOe?#n&_tejJ|FnTlI>p6!BAP?3jBT5QJsPZe3-hHlxFHu}QLw5vG*V@stO5 ziST#pybo_^F&pWqjd~GOub2HoeW5W%<2WKG5>Lpl_k2&V``+BT#FbKImnCmUZ*~i@ z@-s132z!2LPJi5>E#*?@&6Kg<8Hl_|y?7I#$I%8Y6F>(T;uUtoua3jde@~y|Lk9kQKTCXZp6*Hjw0_@SEV1ot zR@T;_cff5I`P=Md)2%KTf)}9u8$o2^_~O!ZJ~*v*|HLLe(&lmS^j7b5-FDjgTOWyc zZRwR*f8YcF>cZQ*rpnPt^L#N3+@{qmuDxt7q5`E?n+Yu=3Dp)q%Ba2Q$i;7(ekeC+%BWW zrp{NpGPk{xT4$+5yj~lRE=9Dxc<1NqVtGLpvE0Gzlg}|em#9?qyKTk<6G`&wQih!r zN0-|(I9|FkZT6c#n7rfV-#a?^B-WJlm#G-B%!A7ob_FxMQUU6zei5x)bIU5+^=iUd z9^JZ_`CoZDU%xCpT#mLnY;O=8$IubF`yA`U=2b%GnO#oLOk&x$%++Bei5c(e)njda|V zbL_F)GFoITuVT`*=zC)hr)@3_6S5{6e-zbboa>$1aa4Zcu*F?f6SypJF3_}%6dFg* z;O(`Uub`-o=n~LAAi)Ui5+~*uND~00Y1KJlZOsZykYvF|6ravKfs+GSD7Quylgvh^ zFJ0znEo?u)7p8MitaVQ!@p=zi4t2o~h6fLw=-rp|3S0&~!5=5RthBf*(Ay|iw68xq za8g@bT%4`^{7PSrB2JhG!@|PC9$%*uC!wEv>E7`j4OI<|p`Xsl;Sy|E;LBHG;pOC) z*jp40m;xvbPY=HNdO7m6OnnlMWR~MMkJeQjhfpH#vN!z{^3>$;Dwn*XxWSo~3jPMR%&$+|tfF_S0`ZaFYgcWFYdOT~ZezaOZ)FTc5fi-VE` z)W4(GMWb`@dA&EhoO_f!d1f|xMEp_zrdjX+nHL(HLZuSTA|PpOxdsJGI5Fyy&91Bq z9oB57s}a^Ys;zUYE#(>i_s6KOX;n{BX6xdt%4q-k_xN)Dg6`vK{!Jx?BJ(!QWcO2z zm(&c|__xvfak*dUZ@6EPAI}t&`OEUp5vM19>qeBMM@AO{jq~2MO>JW7-v@hR(wx={ zdo+}XdppV(w7pkzTo7lai|0QaN%6+E-fIgycBwSDx*uLE(&+8$MSOKoC*Bcu>@4SZ_3q1C^U(tv?ju-#5d}Go_y2crlqMQFa-}r>$P4KCQ|&ul zWGbmkF9oJtM0swWiH{biMtXD7nE$Ti+ous%+}d72&$~OC+|J`*;Jt{J`ICr_@-+kP zayy&d%pRt!LfB59?-^zC<*f2((Q+B4(Gg}|1@A=fN`^d*=RYpp@}DysPe1io^K<_l z4_`a@BhK4hKPoAtxsv#eXg_)F+0Knfe~sA))X5+{euy~|{xWWI6l& z`2N+>u~E6*nkxojk&PdvVA^4MBUP49G9~N(KWL=(^4Ci6D!8RA!?-9It`Kt%3jTNg zxyZBY0%H=^H_cjbto~D*)L$8(_w2}5OngZHBdzkkZ+%Das@3T<;cny#p>zl>7%sLz zZUL4n*UEFw5SPXI*TS)`^SL00f4%DjRm`b|9_x9@3A0|1^g9sI0Wxa|WR|1VidVY0 z=StZ$STu$q^6%>g^J9pk%yT@l1~2;6_QHrM9Fgu>TwXnDyI}+6N`&&uNRpWuw>86+ zbI%7JSw&M_4Z;bT59KX>;IgN_6LwyS05kwGXWcnB(0Ec|{tpzM3WM)>SXWZQ+_d2X zlas$&L#YKp&O(7%26J9axX*`j*apll&yyFCaOhRcoYsl>UW$yD+W`nA1VCKmg1-6+ zlh-&5s%3%y0}C=(Dm1}^4mB0jz>YiQr$l-z0!q0u&z@!H=bOHLO9cQdh|Gipx;;9k zgVZ<5Pq=eDAMOMojGf-A^IqGPW3_8l6z5n3G&6v@_>Gl+V81q)bALVwz%7sK!h0M9a4dFZZUjA=rVn1sY`Y>*D*1NdO`0U*!c;oO zFrw##505Qf$0<}uD>HM+joZ1$(eflVC`^ei51w5RGA9gtNZsYp9gZ~&6P`9f+VS+A1xL0KFW}a3d6BNCVO2HG1ybDze3T*($ z7L@-t*7SCG8`6D;Aywrw^j%=NZUm4On^wV({QNhP$Nq%y6a-e%Z?IOZtgSHsr|9wH z)<1s;p{FY?Ekz-nfW8CA18>t0B2ExWO~^!*6f7i-TSqAVmH-X-PhfZjC)VxW9EaZ_ z;Iu?eSJF{pKvweGAQY_$Fgf7&+;$g%&jaVX9SoqWl#libPyi>GgTXTieB;-()useJ z57~h@2)C-LzW#3@(^M?R>c;xHDGV^e=xeSSM{GATyvX;;2W+K?AJsrb5^!9=t}Jj` zXus_xHXXIwv%k;FS+h?BrvaOuf`S6fD^_yu=HPk;G#3G#SV*QNWNuY;b=NtLO>|n< z4p=w6Cos=ksr1pPP#E};n%Y2SpJ|z9Nd*ZaJ*VG%DEi#elG%rb zyHKmbfi%&1Hc*Pmlh8Jg0)K%thys5V6f9-L8K4MgAR5l?2mmB_y)sWq7ki~LjJ*pc z89=3xgfvsULaR4<;M0|V|1#?8d>f*_q<^r#f&Nz&BdX&EbuA-Vu*2oK2fQFTpPb+t ztF)>%((yi2_OL8)=vO`k6cYRc`pofh1-19vTEOgq`o6UE&29+={Xqd`QUhpTL$eQw zFLe!52!ctkb@_u+n{U*HETy*%2u$rJ+%O$6>2PNz3r3-#7$PO27*Vohh#Sp;1_aRn z6lHq~YYTf9ya)=$x3a?GQ|Bw^(Z+QX1R~U_DJbZ{H>f132Ef!4jyM$a4-iI<^P&8X zFyn+2`IEbZ5~`@BqiDrqlVA(#5`dodcut=gB>SAM!ifNqPeMWhXiZNUBJVFJRaRC~ zwh0w$|76G^!vUF)Bhggq}mh$R)c^igJz@fYp;h{$!J>AL36Vo zwU3S9x(DAhhk5-7WCc;qDCgCvs_$@y0cHghON}A8kpiD)1HLb>$GO(w^+3+_`4ay! z>2FthOEjb&f&K{ayMkN!MueNVU^P+K(&}aVI`_JhmX0o~w${hsj!24ti&8=w%O>bs z-*fOxyZg6DrHz1&GvPd-xKdf0efceqAc_&-iBEvU1su!m9^sE}COVlA>4fFvW>fZd z4^1^-yxPpg_9*%7;FLA`{*UgtBN44Lv-bP>o`QBU}%-#;GXGC$leNO@WU`S8+ofinF@&t~zJm zyxgFX^@q42dMw9-sPE{-1_?>3Ex#Pnd$^B(tHg?uGb=^JGfZ)PEDrO?SoxBN?qWv2 zOY;aGrB`f*XKPw<_*XpO-P)RTXhY2Oyd;B;X3V~#QP}SKmdMFMv={t6y~pRA=WBwt zb$gRtAIAF?AZ(Yq*1o%=WSBxdj^>AI-$H{s{>iE?ix8elW1r_eGJsYM5upZ-A6+W zVBSl-187LH@1+NQ6roafFkXZ@S3Sc+%y_|7Y}Q}&}C1O}{YQ_D4pg&|mlCo>88 zHVB;_taH;nLymfm61_WSY%;O;hZU3@W|^<AK?dvG52^4%)NJ>AL&gD|gj$-kJ;x$?_^IJI!v@1)By+_qmnv1{|?>y&juK z=3eG)_e@l$Kh4!Sxh4LrGhEf&X2}tm<{&&yqiQS0qJMI=(mZg!uuyWib?!T`W0@8% z5_lgYW60^!z#?JK*&Vd3Zucx*COREAJYS!tt=`cvzpL#(*aWumtU;++wI;`E>^H;tXt#$Ol-3U?ajW*H zu;)9*4(u-0WCyeylP*rbqPmnPq=aY)<9l7LjH-93Qxa4Q)!xd#6BCdqIJ+6e?Q49E zF5-^P)ZH><#!6S?r8cA=4L&rjVs4tl*L4Q@>7Sxu>0#rkT9#U{@UHv3cV zQzw39pYp)L!@+qkA%*ZyVWby!d-8xRd6(f-j>pB7&#=Vi4gNA8ffP0vIae|bArgvB z9~2T}TXUd^hWdp3Z(y=0&Ds~kriE}^e2!L7cX>*=DA@W$X`J6~N>AM^M0 zA)@EqW+fI0&w#K9s|T!afN=cd;;nO`S(|iMj>u{cw>qCK(#uD!aO3`Y@=>I)TkJ&q z3k=tdr|X+iZ)}~(UVKy|ZKR;fBXo|J z;Bm3lMTR=PxUQ!={_JQFtvtUv+4~8z0H4EqjmT%i&om?WoYZr!ImWi`dAoW&m)0!u zlQu58@&1)ioX>ch9&&eCH><^lp);wRRLq~SV1~7vP=%z0^mT?*<7jH_P*r)O{#VNc zDEVBbde9)t0y*f~+GH^6Oub0}x(o3P&-L>+W#)qWmuMT{TZyG)=D7ZH8YArIr z=9=5m;6xZ7ThUL#zg~A{g{Qjq_pXBe@P0|45(!l-dpR*L89Kyp;&se#%ivaqr|f-Q z(RYXc>x*bv4SKGdnLd|X&-J~f>mSbh4HN#Hz4x!Y$Zeaet<6)Lw0Mh4U$;EA$=Dp} zz4$nJ!y`sCUssM=u&j0XHr`X@uSFq(aMu#L)ndA& zn(xa=Lw0ZwljiM7V(-;7{A0RO502=uK4|YeV3ekZPi;bZ|79qbqFP~XNc`8zhjK<1SSHyl#CFG#t2b`mcnxFs?iG zdd%ebc>XEX$`k4$=FSB+&wn3J%~LwvN~M>Y9ULdkB~;C{-<$L%V$etANojYi;y#LV zaHLjNMzYGhBc5NLED=bVo9#%}+I~O_<;0EpRe5h&31u&A9058yh7J5QT_)Pae>f3D z2G4@0r|$z{8Sk*#spXCy@{G3naJ zpuT1W=@vR)sR=q`{HRqH3wI{cOZfUY@~2YdNh0BAZd-f5)O(IpQt?*>FCtC5O~*!M zJ)EjC)^t^Oo!yx|cfWeO3bomecYA7Uue{En*{?K6E36{eB|zI9&>2h8#Z>)_goYyS$|` zB&}aDh@aH#{6YI?=3kMjdJb)=m;0v5^4cMxVf}yq@|GBUW;k1*WSY}r(N!e@Oilye zh`~qGmnCzY0p)|5r;fDCo+>0UP+cORQkV;#iuHGsmhZEbM(ewENqs(IV6IEqll}(5 zI50HS0@YOE9HQW>svVZ^<8Lj0@Ed3Ugot#PD6>^NnNCH1Y%oo+w>2n^zjBgEovG^< z{$v!3q@}m9UKBhwX`|#WEv}KR1{iFx%Wqi&YmS?zaBT<|LyP2oK4%in}x%d^( zi;dbGB1#s^Gnk}ibn7DPPeb=A$JI9VhY_-e2dcjQ;|Ghw-g`A#PCLywYlTQNCB3}| zReFNtzLyl`J{##JOI0rg9w*J-OSyVJiU8dAkfTQ``31ilXT^qAipQsKzFS9Riqptf z+%XJEN>xjB?1(GYYMwg%52Xsk*%J2F>twcXCe^-NZfp-!Z+$xAl#gF^j{?@NzEe}; z!OOM4Uo=o;LF%`dJs8wM*mViE19=7b(k8&CP`!rssNkg5xWEpcxheU77!dZw(Mnl3 zvFs`^=v^`(W$}N?@IE&;_;7D!Le$CYs;sn>q=t@)Dh-K5Zj{Gy=sR+PgJ>r$51Rlr zUk28#r{`HU`~GsCqL*jvjl(y(ZXaCO^rIdxSLqS(;?qCvYpGM?dBI%AhgINc8EYwJ z;BOogtDi~J=JiW&bg?rM-}|tPbS6IkO@=A?@HL5!TM>??amJth%M?5Fu93-4HI}CyccwTkDV5BZB{Jt(!2viezap1@N{pEl< zASkn|2Wt0^p+X;kVEqljlzsB#I;z0}>chFr6u|h5VOkM0hZ=YQ(zzW0h2bp}Q)c?| zu>9omsIC`Y-5Uy(QBbMeroH*YMGq-Z@`&%{vab@nRa9v8?=Nd|=zP!T$m?bRq2UGY zuz7g5d{WpDVh8y8eZ#Y{kqQ6+q7Fbh-31G3N1SYE%H!(J@0SC$rdRw*1munLHLbe2 zzs1I&kAS}P09AuQ;~>XHu~2xqRtQsVUevpS#u2I|Ute+YtCL>vtIY+7pVNc04RjM! z69>GK6NtEk7*SLMw%6;ZY8pl4MvZw2-X0^DljmXM2xFK-o1RHLym_j%MpLT(?zan0 zG4Jp715E`p)MWX*=YtW&f}`N&M(wzQpHXgOv?So0ZDk;I zJ0#Na4h779uK>0WRB6YfNw+<)rtQENfQJdnY$u=JCHxN3!~eGRsUJ`l|4m-IjR~nH zK2XH0M+z3tPxjEfHjL?<+>BssGW#Osl;oDkHX7=1!;f3IZeGsZ0szj%hPiCiQ=uf+ z`GY&qkxf>)IRS?RKrN`(08$rp_)`)CyfBACLi$Lj_%$W~+=2Fe4KOM|UWY;9N4oQO zu<(chEpEG+SowP{D=1k3)TO6~<95v7e#1r>?Lnk$s9PfCbTI2Pd}fEXJD@YbeIlDR zQ1EUXETG~i9m>#$OB)#B1~P*}_W&9UxU{~4asji*AZMY6j>ts#1u1O${?$_13^j&t zzk^J;A8^Nqp#D!>7jg@}kNWK-rC?AD9zA+Q??>3kt9WFoGaSN;^?YFIZJbh83wW0N zOm%D%Om(EMmV)#25{xM3e)CVWpV^ihEsG=2YgGSZe1rPaSM^`_>&Z%qNP6y5}m;(*!3^7hB;Fhd$@yViy9 zD_Gy0PRN9dOLeeTjV10exxk5h9RO|>l$3(Xsmc12N5>pD{yuV;slVm`RwC3_st_9h zc2TD_B76?IS>*bplf(ocChwn|0G$ zv=o{kx@XsKu?jG)4~rBIvNI;QjF#*RHZuKoHCd>zOvEQ6ag1*A#Hr9saTaEs0iDo>aY@)sl#4^0&R z<-PfFXJ-dtDh^>VxHAs7rj-G{9@q6qP_P52*D&~m&|xy3ki#fVgMkIfLjCqO7wCmi z);O@Wg~!Qa97-vb38Rr5C`}Wh`ZVj4XE|1F8qDhV^>%+PNC@rfFR-Dy(ECXE0i+gw z3pxp1UFsZl-0-#+B#ok=!OlAV-@Hf*3oKH=eF7O+-{}HoXp~F44<9}RcneTTv2M{a zM#|d5=Y;#N6W$m~`sTpi#Yid0&{i+u#?ak9d26w>7M-tR{ph z!;rQdaaC3+um3k0yJoiWDe+{KR}nKu!kGItBbR)8|3ZdKLKLRuq1i_RuZ&9*uo4R z69{(le-&VvL`mJSZc87-z!Db!%`k-lUJzY?>;VsFm^w_J!f|F|<_yrHqNH@dS1hRb z_|00rnq++KzgdxcR->+9Tg|fQ)-`)R3#D%4<>h6Xm%s2g>)oCkt{X*@9RiwP{EJuh zjK1fhv^dk=+QefQ|FK%2u-R6wW(uxX!{8JR7MGxYfJ>QwJp|4^**oE_YT|dWgG5XV zJb!wp<=(wC>ih5SY13GAt@I9X-9(ITkm|b(jf`-fI7Ccu8LA}$kG|`zNk%SfZmuSV z-v8XjqypC@vojA3Y5&bTLw+*DGKvw|%&EY}C+Bj>u^%F8c&_zU>t#Uy zE;-m;cIP(Zm(r|Sy>ZDOTYULl}BhscK0&>Y_q~n z1g}{I{XLJjak1m;M5&V8nzjGD z6Zq(+e!1wG@vL9k?O2H@PUVftc*$6G^_KP4dycP>*Fr*+dAP+cs2hZ(u-B}c)Vr=> zOR&(?BNT+=7)SRKO!flU*{~fvB3b+-(0TQlDA*&vzqKGzV-* zs8t;0R_M7pHM|N2%^0<_0r=(WFOH^_*~!v@7YAHZfQfbq(TKqs9F#V*;~vz#bYBjN zpycmRQM%QgO>UAF{x>Pe%*beIIorCrs51wZa{{)ffT9r|fG{mW?bJdej!TE(z0s@?9COXM+O$+5u>yvz6 zJF5=ltVNIR9}x5?B{C7|Nh>MeW1}@uH<4@XG~LNjRSKz*dT}7iSSnDQ%QP3rzFgUR zTSaSU`l_zxxF+}(ryb6Fy}AAymYHlC1BeLn73vq*rkR-Rt?Y~}w-8^CjNp4&XPv`NVdxTx zhTPA1ALeDB_tz#poIXF+c$I*z5gELq9Y)8#tY#yZVEK|INUuz3an+c3VztZhoL`rE zxupJUs2dr2>bsOe-}5G3=f@GEU;_ z8IgWbh0!$vgTHBLTLO$0BUE;aFKmqBIx^)lqMO&!C{8De7hy!-t$q&jH?aO8KQRC;1f29X<8aZ| zKw{?OPOaL;d*LFD56dSS!7Bti9|5s^v+l|V6=d(9nhMLb+?cBQ_99DZr$gB%3X9hL z9n=9OcGF_68-IZrJO?EUV*Serzf`?kCxh$FMuQgBBDIoo*EnXi$g3x;+so{Uc6-F1 z8M;`4s_PrxY~A%Xh#5=ZnvWwYcwSJ<(Rpk_q!arGe?Wgvf1t0ie)~Q`@XkPK`;xPl z!BDc-&kuhsVkJ~f7;)B&Z=Z|ZBA3bf<9uB@HXHZ!j|*`VZ=W-9icAZaBGr0s#{w@x zC)8Q<-pt;dDrtevbZ4_kRC28ulU5Uk#zVmw;+CaE)hO#hmy?{ z-`PJP9yGlyJ%C?L8}ohoQaHYcRDbrXckDy5F#kqLY;&!<4|6#Sm@2}(Tb?#>DR^p) zs>xin;CQ0P+#fFZxooqA9b|I^2IJ#u+Q+5*PP-e@B!YC08PjiXdve{xYbsSCrPPUS zx4hw#f<=#!nu|s~mN4pe%$}QeSB?+|Q}zc!mdFGl1V0@>SXhQU5_#;;_&)QSySo3< zsZ|11+CD)D&!ZS!W^rs zS9?G<%n^!L`b_!0d=)~yrBmdiDnnMG^0uA|h9?}T&)Ss4_Rnz(>+7= zcQ4E&SyqfE6PR9pd#%v&Vz8}o!BOb_JMu1~2|a34FK)>f-X!#Z3hm51BweYLGxjDi zdT+J@7uG;+ZltmPg^fX7)Vp4Jk$9$=BZtFAF6)FFc-f7If0ZkS%m1>e}6l8Wg8gb?E&KoyA zaTa@AJpXWwPW+;!OK+r4YI;V%`sMm4!sxz?@=-}HGIq^A!bZt>dD!J!#K(t(BE2istWsrXkmj`p|4lOM7%$-g*&Ou>G75#Q@|gB}B11m{DB7o=!lhw`1Py@KH!25OTB zeq*Dz(E8rj_UzQ;{;OF$EU1a`Qp&B|aHqG)1Fc+r0wurwTM@7R=l zJ*2VVOgve1#Wy6=VJT#-yZ%+_N8q#E&&pXRaj?tnOZ1eHFj_HQZfR?$kayF;)5{GA z_Et_s9B1rge9)si|-`D!GcxgC|bchl615ut9^kt$v!6BouO&zAfGXc zvXz+_*qR1nfBo6zep=-`(I8dVpifpX*7;zgbT4a_BJ(?wn)J^~+(H>?4O4|C#-t8a zEn=pmJ{1kj-&_`!LGRzC_6aB+sJ8A_jZ8m-)LP_@<>tk7XhE(wJTth?}d&(0qtcf(f3-}rw@0b()f9?AYAO65LeS)hXEvYJSsg#Apr z;Ba~Vch(j793(oggs|-a2qk6xt?!*j^vTnlTG zCuLs$f<|=GJf(bLFL8y+e%xu_IVE_2=uoHc=zbD)=C<8Ua#c}VV6%7 z@w}}Od2-+*EYh*VbAg%5D$pnTb%dZ@WOx74%Ww=`Q`LmldkGEh^3f?Hw92=iR7fcM zgo7C6&v%jhZ(^z#O0L?GX|=^Wg0%n73!qLCPdxb%8$P2_@|~yfN%wCeh1{eInw5Um z$B_F0YaX@RAebMZklb-kgk=_d@WB`;btpS851hRy%R%eb5sdv{N5=TuqvL@rKpuoO zJ;6xsEyxWhI4$hHfFcmWd>!nytTBz?`=8ACeg8YO^H8d!m6QGX=|a2ipK{Sohh>(ga=`bvjali;^0j$CbbwblT+7M@tR&=mojP*r4* zg&?nxe>q0nUf|vr(hM)(4yV1?=%qx%SSloozqh`vNJq)Y>c3>(U?#-5K(gd?8cj=V zU<s-8wT+yEXyJ*?? zrnzrNVssJ%U(($0FGDHpY=Y9iW|e&OV(#l$ly_3h*2nPNz3m2=)E0zQVv7~JCUJT? zOVVc6{M3`lGWcl~22QU=J-3c43)ZLhJUjng%o#Ypv%siViX>@?h_Y#;lFPg=6K2C4 zoBXdip3zu5L+M$sYNE-UdsN83@Bt>CUdw3Za}wc56J_@D=GX;>NbX?9AZZ)4Kmt9q z@}n1r=MI~qXxK$^Kc0?hpU+&M`{I2lSoO1J_%eSQ!9Qvp;4a@*`Ht@n{?G9NOS-jH zo7uzsk1DaMgWsI!vTLU~JEAbvBDG(q>X$y`+^r-dF(@sXxYPb*kC#w%-Go^UpqFOk<% zW*$tgDFw=hVL9OFSM__S8ZOG?%{xbmJ-!(oW^w((ZXI!sfu0!THan>>@Y1V`8^NAF zQxqN_$U z{8h;$PO(Tz!pZpa>eDgXtk@;4JNfuOcrbfz_Ch=|^?QH`tM7{v1&V_US{y$l;kv5^ zTjtA&l+yNYosHLJtrdJd{zZ-IO9LKK*KP$dL_a6zK;D^k6@Ea{N$w;&<0$wbE`R2A z+<+wST)Ns&J9(};tA?lEOPAZh*z^81+8+1z+P^&jyCYW7&??L!{51nUb*5?1y7+=EU*Ca;xVV=2-Qg2+USUUsxpn0WPAt)6;H2itFPZ2gelpcOD6iQ*WQ>Wd}*+sJAStLF&h*K-Sle zr$sF$Gn4C+(Tsy2)V~81x46aPO$I)><|Zh?Pt60&v(%o*~7<*GY0~XuG8j{;!pB+kawwG5s0iV}tceGhV8iB^SLpp8}mp@jl$1mk$K6k41;k zaxHjoeE4Y@KBQuYi}VU?)sC1iRQyxJ^y!I4`+@=j$vx^7s8TP_r27N;!0(BMiUy@d zBu;=xc5cY{YW5Ga)*2tn;SuZlwhF1Prnb?Q~f+_I%CjP1Jg&03Jn?Ziq zhY4gpRudP_@uZ2<{b~d90UpY?m2-D7GNPWd>YKPTHq4A^nk^&z&^2?J3)=#_7VEcM zW>rk}I7kDY(0}7hXW{lq307$jZu@sPiQ~@{m64c+M65O}Sc+mq?xGyrNN%FP!MM_q zj}(C>UfRgW$P&YwK=m-rLSsNKD>xRfbjiV0h(v7_{msMM!owNwOG}oXXN^C37ZMK7+RF&utJM9T%3ErB?hp8?G-7 z#|o7v)g9eE=5L&;Hb3Q5VKQY7upppO!qtgAIkKw_$z}CAI%EEc_VU$h?!dHmk*hh& z@hk>;B5TiY*_tj)nQD1Ya|^W?1Cb^KDQzu*bc(MNn40dDB#Opm(_6f8#3j)&6N)sj z=%T9J2{==xmm^~CRiN)8nva$<(U}YE6NIf49hdm1XOA~-89Y&CY-Nga6@*P>lY1!q z)nbq*6S?=x)p+PrSD8*;V=4~B$N!pWyhpru=%$c?*N%8vtMV+0u_yeujy<^?gJJev zxd=}64gXL^xe&!}PLJo4rtv+o5%sR}!}|RV?*gne>NtubW;X$P|3=9OqGC%UN!yCbc~=M1rroe4!`vUxCs~wdOOu3 zUp-be(Ikny-y*ecQ2J0ytd_m%**_|o(7RK`E3`w!)imk2^Taxg5-~IrHV8+$-KA)f%ojNFR zL(Q)pfxbPiQ^^lbV!Epxy1mg}pIF%HvBb2CEO&_2@M2BtWYO?)k-Abju50mrVA(V` zCUciT?xMcz-0y$({Ohc`tqpP)cnjTuBl!!kw-fu}y<(?8N8+?-tNS52vLi*ctj7H> z6ygjeqipq#I_7>Kb>+9_v`83iQrSjvkUjobuWYz{izf3WGKQ_eBs(gYPUF%IqtzuR zD07`xYxCN0&4X$FZ@;6u7Qccd7uN-xK=C6d@iy^`0r5WY+=DM2H7kW_`9kg4<~;aV zQM|(Q&cgG~pu4e!UW;rnP2O(57Y?&>LMfGeO$VeNJo;e*2b>7NsiT<5xc=y{A9kF< zTbK%>j2Ph1h74R-q4z&))L3>t~6OikHu(uf$-JU6!CmbMZ4B z)Ml+yJ7I3@Mr=scwp};}wj|^krhKyf2a@;VGwXTjXSBB6P0Y;rhHtscX1EZQj4}8i zUy@8SY*yRL(#Q8lnr6T6ia-l!`XHs1haM*{_5aay-ho*5@B1&=NwUf)k?qdjlCt;S zd++R>Q8Kb~%U)3kA%yJgy%MrV_6*tUcizwE_xM2{QpBBTH`+Jz;x=#$|K3r#fxu{hQv8ZlU)LI)7%~Dy46?%p+S}l^k~O zSB#!WOAoQpeST1}%9c6#W?;*Np2z4+lc$QOIeP0~jj(xyH?dAJ#@6ij?u)*%Iohu8 zfxOoTRp>~X4s`zE%7`xa+1-XkIBe@X;QB&CH4!AiH$YzuB2Pj{U?IjOj*x&_5vuVI zJ!a^NLlZ}nG6Hph;0|C@ZyIm9uJnThWniMh4%PmH^$|Z<7Ixz*X79Z+U3vyKo5>!ayzYYx4^c!Y?D~mOcNFyQNT7$CS zjXA5B-j^`d9QnZwD_V}Qfvx%YtT?`JNWJ)twH%~3L;xo&wsPU!kWjJ5_;#<~F@+v3 zToZSn1+k%VTazEusKr%?U(Px>ISp$1!(UZf8?29we1gJ5Hfzlu-4^;7#bJsTb0x&@;x%yNL}>FIj#V5Ob_b zRaVaV*w?n1CW%=7kk_N&Iu&Mu8`2*SQ9q^%p{dclcUcqVjjB>ryUZ0rZ|1cGGIp5Z z3r?hA|6rd@`JJRuE!$(5^o@u6CuJB%TnbRof|#tMTfdwA@eo{4*rfa&G&!Hm^z+By zuX{qLd}R&giR1DqVbx@KChWh5qTpflvtsLz>MwhFp}CiXcH{o^=kVU#;tEQ#rxIx!-SLxBuEH+pU1zghCSu6>qBETxUrRT^}7A zVeQ^QmD@p?&|tDu$<1=3`o-1asx>$ZjCro=%{%=OjO7uCX<=Nl!#F8u`2 z9Qg+IMX5YP3DE@#tOldTSBasDFd45{A+&~O12$yNW*)65(oj3Qxc%P7M$+zzWO$R) zdYx?AJ=9^{E6&x_HB(4SCAXp%4n-8vSZXaD98CfTl`x_Wlo1ddkZM#05kh31iy*59 z(?f4>FIY&LAzlHxQ-45o52`LODs3XL+$Eq@5iI&znem(N(>`;j+G$i=`>$WE=jTtJ zX5Oxv?1Od`=qQay_5DrS_it`^fyDk*7AG1?2F;7LyiV81Rjg1i<&yqNiS%zv4IB-aah{RTtrnE_W&23ZI zo}OLJYvX;ayH;(b|C=nKaV`g#%ul%52mig@3g--V7W2bWTXk%HmiP|upzI-0gigvT{Qu@k@Fp!Bo#ks5 zf|&Z^FhA`W-DGQ3*ch`)BsE^o}N|pSYF3} zO~CrPF>3Gt@h^et`!fCA%PJ~bo!)ZW9sMTg#d!3-$m6eCyQTd$kk!%tPKBxhiF z0;r?_d)ezr2Hp5|7^Fc`a1B9Y_3Grr8wx1k4>X2H^#@v;mSB1%i9SuNHZo+j-Cac% zjnlxWrj3-oWUEotX~bo{@u_ZHrCgDPPO;{uI#(Hj$mQkkXGP0B zGCY--MxyWPSSD35=1ehanq68w9^**#f-iIG>S(ODhXq))y`1kODonDN3<|=7q^@6R{R1MeQjv|W7I-uwGKJxNrV)lP0Z)&i%60R6@` zc|i7u0y18^5FIWVy zouCOjFyy$nckSA3ohHnDdF&;lEt0sAS{6jBlQgn1zs#3oN7b@x=^!=ra;N@>%a2ZY zOcM=W_B^iAcRwj8D1-K8L^-6L6lo(c5w8?s;w=8$D`+181%~nJwo-q^yf{zjd0wkr z(35{yuRU|I?P*kZ+hFhk-_(P&j`_vPXn#f8_SI-^B%`@Nru>{U|5{9ilbcWEObC5@ z`Mz5axw3h3vOZ=^OILC=)3#CLn@4Jo_|1dY^uy@`lI9ojikcYnEE%kCN))x`#1rC6 zX?)iS--ksT`swx|7?sTcQiwI|YoYBU_F&~h9-sB$@9$J3YYz|o_dI=Fe;)!OuJ?U+M_sVEXhwGFMA_>0ZkkExjV=P~JB znP?tR!O!Kd3vR(5%Vk6z*iuZi^Oaxe>DI>5;l!+evR(B`QShN0gKzJvHqfWlJ>FRf*xncMguj&P2b3lHZe|!~X7@mXT_105{zL(wvlQ+|!PqadL?xIFWr^ zrx=bNh_T2Fs8EhSd#+?DG2Sp<-FOL%V1C@_nB; z6o_KRUgwgjcMGy6zR|1l;1W)?=s8vN|GU66)9~^YR}0s@+=rQL!;{n27B`NnW8ZZy z%3@PPC1G)@6Y(HMqeL{ndo;?wJr8M}^13G{+Xi#1=$3fP^`FUTZ!U^fc_N%AmsyvW zU7RUASwWv!|t@(|3g{=&|8d2P-S!z7O%bz5bK zXOz#w$=dgAnPyv#;Gq1C#B3K!|CFpnyfWIX5#t)r^6*AEkn@wotCbV+Yhck8HQQ6m z$rSD_bTZ-U2E)ZiYSP6HXa|)ww-N{L$|I467BoVREM29&Plit-mpoaQ2X7JSRAvYv z6}8XI3cI&xSS-_HcLn5IT)fkta0LhD{2M*wj%8Dawe#@%9u3uGnv~rA&D)9(gCyRb zPprw9cbz`A6<93LNI!AQupKdWGtDq94Kzu*mpc~^E=SkUk@6-gpSA@K_pzz84nvA6 zQke>0H?DFWw-#g8okMyO}mD7rakO%XhX!gW?uD9#M|uYCSVw z$ETX(sd&a8wiP}S5#uNaut^K%C5W)ojLzGPAV-)JBp4uLr|?Hnq)-CxzY&ec5lOjS z;CltVqSnDBG*}*{E0)9X_yg_Duj?!<)8)rAJvp5^h&Iz$l*SsiuSUzMv*)Nv7SKvh z+Hd|N<*94Xe!n$I@U95KXCXDbpC;G8piKPJBEn9dt0<~n-@VCQ?H9M=fYR7u$0xd# zcjnP_?e<4T6&}UDVchr{Ej797wYmCg{wX%q^ko&C>+Z!=Mg(?j^hQNA*JlMUaPf7r zY>PK1KHm#Tmr+BUXjTY6px&<$7@}LFxh5acs-i!9fm0pwB!2Vmou3s+$}oLgZpo5F zIr<-IKx0y?hbnjEy-e0OC%@`47rl)Gy9kZKS^ehw+H7n_`X?y`nqo56T5l7yeWzuQ zRwZAgO?{$~Nz7X>i<8pb6&zbJm_3*H13mljf`ZM7iU^Uja*;ptp;VS&iiMK+^2$o} zpJ<`)AaIA(HJJZd6CV-dKECetYw7>w_pwqB=0R?cVs5Tv24rMjjAvGm*yO01`1?Ny zZ83Qx3}z&41T0)TSN6Z_v)1|=jDbY{6NITUWPWQl**mGb&&)0?95t}=QJL|B4LRHi zAmqGkltfyX``V|_T&9ne9)y)t@kVcdKOxxksi+tzD^=Bn4}Ping8X2GEAs>o zes=b(e%1`^+#qfMzdIO{QU2B^e}CKneIk4s2%%owhXqK?ixEJWCeB`6%vu50l%s+K!A?ZEDubd=l%wwr@QOo5 zgg00UP^oDUObXh3Q&||2qQWo+FM36#`Y|Z@JE8r7VjrMF#{UbVJKUbO+t&}Q7&}7Q8-m$< zU;94&?|`Uk6R2tdMDv3kFPc&ykXEL%EV7{B3839Qcz4?z7AU$Um0g z1esgll%#VhnCZ;TSqObYJfb$yeRqh65pBm)D^EKLOB%XS?LtV)!_#)t zf^tdS-)1PDMz+jcH)I4|Io;Xm2g#hTLoB!t_d(9BTPf=*Rm z@- z-$s!MQBD%5(KrdFL!1ia^`Ruvaz>}Bk6*+$qm*!{ViCksk^too_5djO1?)$MK1$hG zTSJID7SywWAEgA1+Lwo$^TU~!+`!?52yIRe`?<19h9H82C*lXQ2Qnuv9qaElZpK^( zhq(~8^R8dSqp3`h!%W{7S69z;o;Mv-Inu;uwyf-#;l|l>^y&MgRBTZsCs9#9u%(Yr zeMBC4gPns<)b&%;+An@W!mV8gyy0iVg^XpQ%pyvN-{M(mAItL-6Z<#+Bs5IZsuFQa z2_?x!)5vjh<>O44P0Q!BuR5%Etl+$pRzN<^#qkm&?(wc;6&W&48){{D$h(_4DV4AK ziKj9ot81(?k&`<_Z|xcri}C+y0qobs#(1J*>VkUao3r>if@LIF%0FJ=A2uE$OpJ&% zoRt|@{?=T`_eXuKn^?gDE*jK>2i+B z+0YE5YGrhCoR3#h#*JErz%M_X%iSHf|Bvf%RaV4MIng(FJ}$mEQwL^f0TDXBF!teo z{*6i0@~Ip@si=wt=e_`?V*D?A%$Qk)I)U((C1VG{;B{q2^N1jdKy_wv#Y9=lr&3Zz zEb*!sMn8I1$7Rq=sDkaop8XD;+@ML8CVGKqMowQX|5{&AiS_}#aH5Hm9a~tJH**f| zNVwua4gPYjnsiy~lzOb;5U@ONX25E9=gdfTO#t)cV<VMNATflCPC79)&N=U1s{c^<|PXn#2rZ55qM5_YLoBrQeO~O6W1e9Rr(} zY@4f%H)4iWpXQ%G8G4phR9{t^Z^dxqekzVvOQq<)8ejEqgv0sH#1a+;E05iHj>=#D znSNhEQ;RwKxfcoUCimUGR;o|IK)9j~aVr-JWx$*w2$-NUZ+fq5nH+TDNb<>V6wZHsq`^9FB z9YsxXxy{PM@?oaHJ0ew64X^cP8-kU^UR7>e7Q{1SV|N6a#|gka4=cgoACli5s>-@~o!ct4eI|J{99RxYPA~5IWENNU@^wpiQ(uflm1B95F=@!j_op+c$?vlbTzXFYneNTa z`|i45%P-&)v^%~}%!0vPANb7a_}{)XQeJ{(!QC6@B>R(#Yp<1+G6_Sq>BPEIt_jwn zW`Ttu#mi&`C8V^1aT4Z<3#mZ~C-NSRmORm|)-~%*6YD*m^9+KLsJcK0>$FUPH}GpR zzoec0t&*q*P5FXDZc(BF5d-(<7JnD=tKWT^7^Cix@V4=F@-Osp(-3hV-04Anjy=wl z;@zLgcL#5!>vPl#av_ki`QgQD=4#$9$!J6?mT)!IyEU>E?gSCw-KWZW&&I{eEmQHu zd2flDd-})55xb9Np!Ad0-d0MrkL8Tv^3-akF6JS&GGb8$^3lD6JcH3KYsS9TxCT@m z-10~q7JK7`Dekosgu)AsCn>4$?%ML+LX!L{D!|PVB`YfUXd^h`9icwPjjr|xhk1$l z>~f#I#$nD)z`0r-_k^=1my3h*jSaSC`*IQ|5wRw(AcS+?s+c($>&Qh;3<0!y6w0w0&H|fVq5@>1~$!U zFsaHQ0c-R&{d$&e5hr{L@LN**2=PO~^u@wlC#Pf27aT|=%FZKF zJT?E$zg%36_x_{nQCcZ-J6g$AGWeGwdJ?JO;Na1=zmlh9v`3aKYMbTY@v$4*q%^Jjm#)U2^ZZ?sfxh#}+mMLBMRqg9s8?@wqI90=tR^|EI)0MR z2;JDF_4JZO_#>;Cn5Oq5D|*)HlI8M5zdW=yx&Wm#z36-W7Nm?ZF8Osizer`C>S;SQ zb&}S&RP>x5_kh#K+R-r&$oTDvN_E~RWsc% zEx%X-VwEUBnOZ__K?*&$!XNNPn!!4>r`vZeC-FPPbgh9q>VKv-(^6Ck+RV~2WF_Ne zzobq3A7?-h1+~o2K~)+gF`Pm|c011lC92}3X*W+A;OYY23LKi?h(KkpffbSiEK4vV z1EAF7cXNZ^8I)*y?in~RVlbd{OHKS1_{%FfK*zYGsY9-rwpbsy+e|$4ilULFx3XWz zd+R&J@Y>S)k6(nr`YCDJ4OpX;FY+oTLSn0Z#@=Q#r#YLyV|{xajIg=S?g|P1=`}Z3 zOc#-;tB4z&!Bk9>+n21jEmRbciyS*qz!PK0zgztBf-*Nwnltdbc=tn`uutW(vLmZ* zK}j*k%EdRX<-Y4v{vI}6-DFIVH*C{#%#{q(tXs5%IN&$f+<+?qmvRLtXYXa{pdkgN zciK1^08_5H{Tv?;qm_YW6$N)rK|$X(a4{1fk^om8)MK#u0WT0(4YnG>fGrJ>&qy>c=Q)17ziVBr{byWN-==&xnYS627 ziZtO!TL1cHen!sNP4bmh`aLpAKc(g(>eA@3z=*v;AIddw=O&i*-zw+X_86DFx!WA^ zjJe-keuN-cdT(Vu^lcIcDywkcn=fn*v;w@YBQNImYECk)hzs7ad1Z(joojlyVexw$ z-eWL4X03OeY}wYUbUhD?TMh9NHfRrzCFChvW%*Ko{_A?w9tMYI0^h5pZQ;@%el^*_ z>MX4bxpnf9DPNUa?)vMcCj8?h4$IZ~BOI@JN(vf!nP=7d=01z7?mLZ!En;WdEX{St zcU6PLnbS;wYW;KmdejJ3nRGwkFab;kG)@QnrQ@qunPo- z&ttx7n{3ALTS(vzLAwm@g?-hh787b8Vu@0j)Vpf$aHuz*tphv*(Sxe-E0)5Vb*tgU zJxow~nOr+){o?*}u?Cth+jaYj6t4g^4n9sv4GlK~DGd$1-O;er4OB*;(e9Ijgjz<4 zEu+V*FWZJ1kR=BbAG{Kt7UD|p7wz|LvA;pV-1q><8_n0lSIn3>dr>HXz`N5bc& zyJZGF3+ti|!35taar-!Khna_sCMBvD%L+Q)5^d~wUi^99z3_b@=7YRI+UNPE9|{O_ z%AOm&d?l*GkUGX_hv|?fT53NyO#UOkTfU!U=oZ)+7{kT6S7|mqIa66bG7_EyN=ooK z3lY1C5#Z>)9hUylwKjxc4V9PRau%?VC#rKO^wmjtXJL(3EAy#aotsD__HT_w^L2(t zYTo3J98{|drmwiCO)H%XIFaZZHO*UcZu4cCv6t8NWi51312Guw|TnLW-;0*QD4_K`@!FyPPUxF^mczi z$v1>bhFk2?7FuB5^4xc6->G1{E!8NM=Sc0nDvglO7ly}w4{m!D*V(MAPnk^2&&1$a~v@ONAI1B|+ zvuno(PV%2jVo^8=nm6n=^UuGE)0fFBdDQvKH}>#*VoM$p{RuPX+>xMl);(5Nzr6KBfhcKU0?=%~(cxgUg^X@$N4ZFZz3ub=eWVd2j zt@4;%@jFIuurw0hq8m`&FChGz|5is+P4ObBJKj9@yP@)hT}J(apwB9+=HV?udS%ZW zkAu?;?Jd4JWyk??gMcT8##Tp;tuBCLY;FOsEMlYdc&0|rPR1`M=cq*As`OKTPfQX1 zY6LWW(G24Y98`z!`XepYY;GwRtbXJ3!SSM0cb7X^lqGKqSEMiD;L!Vqs11KTOs!|3 zu7EyBy3d(f?DS6_`E@pIl3MM{s@F8WC6eyYKJp_OeX@SM{qslZC8~@gh-aaqxgSbj znx*;5wCp%5=aau@7PRPl_An`hjp%mmXLYOA*SI$h88Q(Q(sul0`2 zhF=52fR9PTjebRTF@hWo{+2T{e{gE`4TylU-kT zZUU$n7nhh|dagbz7aw0}K>=g9DVO`Ns{oVnE|fzGC6rbzRpkb4C`n2El#+q-=*}=V zD$<56|47!t(97t;%E2KTq8EC5BmKUpGaKyH0uZ4>mo8qm$V*|mQ#&93(IZWodGHEv zZf;7DOx-eRGU)CNcocWJ@UkR$kDaYFM%k)WyUVZYJ3bO!tM=BzB1IbY0!%E$-p6O8 zta-h+jbz^SY9=WrKpfqG`a4>FU)!0z8d)bd{-}-|_E<)mpt&VA8M~o36;~b(%u@NL zOcB*?7_|42SrL9S*&>p8k6*RJV~a#L3tK>1U9BEN#@Ja*AB`M)95dUlw){Xm9FzCG zq3R`kR~cTHeMRNJ?niP?9#5(kYj(w*W9_Uo+NM8m_lR8n5dr&SFvuzZ{-+4<DE= z5QhC_0AI)X5Y@#U6q6y>h7;y(IktLuX+Ng_buC4yl;%)HIbaKdP8tO`_)lO2&JUs( zz1{L0)i6}O5M-|?84{pDp)z4!uH(>n;qjjx51Ltnzkge^EoS=e$-ubYZgeqXw0k#T z_b#eoYg^I1RW;iWrLFPr&W~WZ2TVg`7wy^6&I&LQ-W!zMHT1fk*wgKOd{7dQuH?6_ zb`E6@3KZo4prMZb`>AyxmqI~A#cA%KY8J5K2sjQ6=megI1SsgoL9PNvwSvkUN;Xw@ zRKK&-cu{rLjX2Nc)V@noobVhB z^z9tk$HZ*Wgnb(m6vYwVv5h5%3T*n<47UCMFpuY%%ZRF=;~yPR5rr2#Ok-XmA3yGP zpvB4RWaM`e)L-=>QK+qSkE6v&y!oClcCgaH0GiowSAe|m?vvES#4wO0>4|eK5!Lg=YX;zQc|~vn`?urVeU2E_8-LbOcQ>Y z0FrtDeYo@5|Ng;t@sn*Ae3u)j@*j#A2y6#by4TKm{|-?2jjj%iQ28cM76tzr+E>>V z#Rj-pH~yYAUY*r#80<6yGZdJBD5u$vAFtAhN9LX0DdxeV^rf;!{EqL6&@~YBQ_byX zUOfTjR$it*U~B&)$ovJH+dkNk-h-R7IVF`R3gt9ngR*LGUtcNkL{S*Qv#AxYtF!-t ztlA^k8l-`>2E?KqP#yxUCE#0JmgGm3mn>@`@`;b+n`Sb{-A9gWeBz<`?emf~Q|B=L z>THLja6-Wbm#j~C^ZSujEWLc{`_t*FZ>k>oPY#nE=bad}*iCS^tM@f0b`XRyf{D~S zK?HSw%&6u9&I+$W*VOmAC-MJ&r)@ZHykyF$M$1TY%$lir_UqZ^Mh(`9$mK>}QmT9) zI~j#fa<%zkir+^c`ke?G8a9)8J#|->j}#k?w_3z?s12fWNA8&Y)YyqYY|gr<$X)r! zbnYzWZBv>%Np08YvBy?Q;(Gquou;}ksqsFd|9VvY))#@5pJa`5WI zY~P!!oG@+afZ_JyffW=C1*9hIf`YcSv`3rcV%7SR!`!ktgis`$^4TbY^*xm4y+FhT z4e5chJ+KTy;{}@40-pnu$2gfj3P;Bf%c&K!^X@@P8;xT9N>Rl}pBl8%lg z_mwP&RMWkN_TyT`$VSo;L7xKLZ+#RLlbl zsD;950LsTV_!b2(3?ZGMO9Q_KJi#bzj)CX21Z+1z_;(vl8k@Rv=}W^HNN3cPgxlbd zsB_;^4mRfuD=0vTq^!ARt*jVAO#W^;2BdcL$xfc?*$tJcI~f>4m4C)xvvUxvk z`=vUPHnJeZKCpX4XNZ~fzKTxaLX%aM2K-Oo+<+qFwTc~;4zlF)3?K)#qxl~Zm2-Er zpkF*tyrneyw(Rc1OBS-+`42J5gDtDA=QMwBqhALn($$MO!hn z`@-|Z-1%?H4#k*9JwK5v+`QXz2-Bo zXU3&E9Sq$(I{tz6^c6wieIbwEQ<-;1U12xK;xLX)jS2~PadnY#REPBnl}962yzf&V z856TF)VMDsM-Y8?*@8&)tKArD5CJNx<{Bz>^YEl5;vax;(Z0SZ2qy4d6{8ZxZi2h? zrM02H{@>vt+E-`+UV|0>*4}8b!A)ZkDQuImv-%ysl8atF$9j@i+uIH>m}}X0_>iLi zsH6?p8uQ<3ub9)c(3n|W92q8)dbp}LEdGRCoDZ)*Ep>UubE^`1mb!p=)Qh+(Sz|91M&cq^3{PrD82kYg28agKfewp%6n^K*FcR2y%Oj(1O9*+}N;WV!PTD(4ceBkH>hdOU_NSHkU2xTYu_YxbUjur^OKN)YRE`$j%e`cvyn<;KWuXFo z7m8r=1>1oF@J|(EhNwCMEV^8Qpc6!Zf-K0>muDYX-@^=^-{~n<%cy0cQLJgMvphLl zMM#57diaZ$R`Hes>nHa>3|@lkQc6*e@!n5VpN9V=P6(!dHN29mVxIh{B_fj*3%5M90}8*JH(oK{kV zAk`N+pFC0~2_6~ilOt{udE09S{eDIkv0p8BO| zBP=`qBRK{6IE0&$A<%nx6Gyj2>@TyjonyNdEHg_|?rQz$Lc zuXO8z^g*nzJ35hpI?azyd@`IZ77heXs;`z@ z{pze!WT8_I+kn9P<$I&!qL@Ge-@_jw3js`lEPs4ko;*b&IKDQ7nPD0I(Goc!2(kR& zl|F`;Ut4PLrHExw@bdYem^EN)bVQV$i>qAR#_;AwdB5w4Uf?uY2@D{Df zoRjot+|TQMaY#wy;#DF(d7^q#Q`^Kbj2foTc z`p}4$6K|%-#A1`(a$~*t$(M!`)`gP1aX7NymW? z(xAVl-|v%2?(%cZ1u@@vQ|g?S9-3nIEF^hqS)O0h*TQJ?#}Q>TleC>cwN_~~xNXa- zqNjv|E$Pdj#$2vuW8Suq^zUSve;T@JT&*c$t0~XL)ZTT)dKZwq==#8Oi>PEE$|U8m zdQ7N#z4=qYnsxT+^1peyv>ZZ`=@06~#+_)@q%UoxKO_q>W|$^dpv#~w1m~U7VV~`c7`2v;qn@Z1e{mm z)r?w89a8e37@rO7SfL?4n0FJr_f$ZJS}QlejDjmp##DhC=_;VliUmDDR<%z>LeW`z zS#(8qrtrbV|2*FtCxqWqo?puFZm(Keat3#-WEVCDyOah_Ze+h>xf6xeWfA_b$kEK9 zU5PiDVS<=OIJ4!C`<>`hnc(_+HLpchOSvAH|HP;{)7$JtpLs=Lk|G~PAJ&S-%^mvp zbXxq+^}TVveD9?k&+-7%U2d5WmlIV(B27_o~*&1fZo z1GX}=y0vY@hfcz#uQ9om*stFu8n=e#u62f66Lg+|<9h^jaD19mi;bC@c{SgbKbIAO-Ea_pNeR8D%% z$Pn-8-tBZBTj!k+Cn_55q#>-6mTD7)G>4X;jKrl4B zvsclA6wNIsxbubc^Xdb?Z&_oA@B6)8g!Z2(BGdH*ZOi1%DHkfJ$qsB^nCs{YN`dCBnnptke?Yd zEccBX#Zv8W*B>2R>K1A4p7_F~t*&99dI@~cexRM;fN2+Czps{jZlR+cHf%axE<1vp zdMDuO48#w>GKQwp9Z@G3=OH)2#-VnL7r*#@>5c!>0zmx%JXqkB0j8dD3VpEX^_UKb z0d4xT`S9&uWYbCn3Q35}lU^&>m4iwM7EKVB2w77BE7Lpve4yx6%?}!06CVaE zC{}P_8^&3`XG5IG@tdp6dXbpukAnMGyIKbY+oKIV6ZTyR_GD~cO$7EgLW~p8IfLGj z^~=)}boMfwT=p;in2+O%qrO2KL`%y+oqU^?I+93KS{g4bRQMIgjMjKU)Z?zv20hz@ zhEbP*fKCm!9^~Um&ZaEGl~uRGvr)CGg2Iipm6?@>Oh^B36^tT5R2S9-&#+l+RWa&{2XW_%T#i@{He^~Xr|yjo88FTZ+s*llXpM#;)7Dc zFR9(8cM*i48KkpaIhd8@r_yv5^6ykWPZ;f@2=7DQvBP7OtDy>4x9kKUee9fdVT=zo8wW#}D@Ds{jK)uOOK z>YAG5ss<1*fno`vP8=59P9XW&1*_TvorF?t1T>#OMihm+P=A7jQfR;ex8@-J@<8EIm5j^j)BqJu+F8;xj-b#<4wuhP`ca`H*wtm zadtk|@w`J@^(`p_5--j`L^D_`&8+)m#cD=DRi(>m!rN0ps6bQl7%_aF&WmN#cq-R^ zHCr_$RKK=fb0ae&s7395qFeM%K#;WH{-8YzW4g%8&AXSO!X1y$lgYRUdM=Z{Bkm~K z>hE`jU4vNZAR|D~A`EKY zcX!Kzw;u(IjVibn6&1zJ$ET*MDr#Xtr&$;eNfYvdo4-Mw#2|74HdsrjBcN^%DyDvB z2AH;15cZ>8qzPk3qq4H{s}Wm?1PfU?x%|(cKV)YIf~#c8p^D>|_fGwhs+JZO3RIbr zOQy4S4KO*2Kjqly`1lg(PW0>^Zob9kvdZKvI=o^-hDZ-_) zwjI`9@>2q>qftcVUef7|mr%Z$_Mgl=dzPSwg&$r`j8rKrYc|*QB+3h-^IYn$uN?gs ziMUI3^*Yyvc7)$qK-NR@(mTgzM|hns-}XDD2XFnjqAE+9CCSv)Rea`f#UmNQn8p)} zUm&Qb4GQTm51@?TJe=DaRq!GLNvhJoe_3A=ClX3feH14bn zJVm~BZ+Mb9!*joWS|`^HJyc27l2XSX7I1n0iQG(a*F~^iDoJPX*^%{4&4^oheG=@; z@Ku^@7wiE0pzb<>e9I;Cqj;uW?A{rK zf|Ai6Y?$}EHJ#2|{c(l47<-xXtiHutb4sgN@>FNgocg{%6llxHNALMx;qOh;d8Jju zp&5G-ANdQBub*LMeUFhVEY9^?WG|o}UU3xJNv-b+jX?x_jg~SR{8C_b^~Rx~CnZfW zkK?_UU>?7roGNw`iI%=|`-`%&vZtymdzw@9@VRdJTK(eL`4J`u9OPp3ykpc z`)vb1%l%AGYID)GvJkI7wVvS5xwR%bCU-({WzV(wz+Mw8r#SiFX|&oLQ`4W$+^kI( z-H<~Uo23!Pl;(zQ&m@h(AIhIo_v*&gm&Z)vHfcxDk><~biOqS!`b@19y9jIQq@0Dy zoPMYEcIIFILGj?z53U=D({CnYB~&yqE<15pnHtd?&0kgL^sJG+$SQWji4}h(K$mD* z!N0oCr;;I*n*9Y=^3j`K7ClMPuq>RYg-u!Jy=8*PGsd3eu*nRGk{$je{B1J@fX2YE zY|S0sXURor0UQ5`zUhqvakS13A(CE3F24P?NUmV&ud#N8#x%mF9Kk}I5K*PX1A5hu;&Du7<-f;AE(Hse*N!}| zp($946FtX#V_+>Rh8<;a-!UWd82K%RQnEQvL=Y{~&qQ58SfDPK@NG=?XPFtVt1;^o z+w!c&18eNZ@2>@29mx>(rmCU>H~$jG2}0o(v4CMIfPTn@*XC%Cw{ApHQ`7%Ejdt!xL7glOtA zkK&4>6!z=fusGL}pV}Gy-k2ohDbB+p!wm!6>M$nC@Hn@(XnniQDWahu@Z#mkr}|GW zmL!-u3W`%5&Uya!{z{^nO1V0j{BK80g}0>sRcSJ>nB#osFi<_%WE%jbxy?{muKvFj z1PjYe_z*O)4;dIjwGop3{!jfIMHE(sEXyI{Evn;B)5ZZ!52q-0ctk{3PmdWSwIPv& z2-1jB?(6N`v>;+}YZAGh*Gl3ahMh1@t{yd9I#!#jFgK=91Y=m4#=uTnW6 zEco1>Av|}!OYX>tbVN9RY&gyZU9IQsH)+z*{;Rx*%LkS41c!=$65gO?F570mr)k!1)qLqi9=*P|mNSvKV1$jcj#goILIrE$;I8dmYJ)gq5* z6Xhn)fo!&V*N4c9S<5k58O%CD9|@q>I|7C#hLs+(mO`z|#F;Ac}+kbPH$EvsH}dH9ro`Fq51cRTzUW{(C&E5CosGstP_9O zvEKI}l06rC5FS$sx~b5|`SkTEBhwddQ8@Dy2So zg^S2OTB5iZi~?nCUFZ7-Ge=Ba#gdvtZZVaxlz6`~%1*HQiN=xl;x#^ecKpIP->d*PR&XdhWF22@|J!()s4pAY`3k5i&J^_a64yEO_7`I$Ddw0 zRw-A6uO?0|tT9>#dfh!%tnXuZt7PBLj~<~gVu{#BK}H>JzGx2||NuZxLx|2M*e^u3V` z?HzP(m{uKLl6HiqCdKNBH8$Q)Y!M5Y7Sr$-e$f*8nKdmG>)sUIaO3xBb6LKT zu58Qt(Ui$8UQ(XgX$Lb!c`Bb&X$+&P7Ztiu(oiG?0yBWKpkQs!OVMTi^KDHoVSP+y zZKODjG}2t$vY;SW!X%$8BLCv&M>)T!j0``42L^(@TOLY2X{XK#7=E8!Y4nx_`QZT| zjR@|KJ&GMc-vZ8hInxmFgd!zPuDCK!a^*a|x`F_iyMVJ$IV=vj2v%EdOYbOPmdf&) zKYmS*j$deJUmM$kn$CuZEf>3gB_eWOdk}7EMEiIb0=p$AsrwM&npHjDzuI+63row~f?13>aKDWplcsMV{ z5XyG{-?8q1-a(6biWH%GJov;xW}x)dV@nsNtdpOK4Xvos1rq_<4m2}=5)Z-1*)`f42@aXOu{Dezl{WtG+@1t)xP`#)g=W`J?sZ*Ven5& zql9T~_v}JaFo2PG0rnIhVVeS>DxqgPjhQGp28=_>y1Ly^KGAOQ6>zLSAcdIH#V&?S z^WB9u{g+!6?fzi$iD;Q;CsZu1UM5FTXk}z@01OM;Ae8@ul+6&83(}Lw#z2e<5FwbT zQM+WY>-`sXQRL_s0iTs4;L;am7NTuhl|{g=3kJ*G4Wlda)V}urXf3m|j;qeBy5ATT z8hYHE7;Y-3q>dEbPDaA<-thk*wcDyvXA;MdHyFPD|g+A|YX%yj;>YdW3LG)4dxm!dh zj8Q`h|L$bA9X!0yQ0fAnJrgS{u$)U_>x)Wc@b(sf=+@p|dGP6@5Hb;@kFyI3!lA$X zH+{#~s+n3iqOBQJA;+jP&FdRjgWOT5R+iBA_ZI6}=s=_50>ExoeY#$F0(z9b?syiM zAHNvZPrR*p;D2m_Vol%kLq{oTAt`;S?XJ-xloE&nz`|o~YrF8{^^Nn3*(<*Z-}IJw z9aoU;p<1`yRvf_f4l zv-bn&fWUHo^k-|1RKpPe$CRAi-vh6&e&!np98Ru|5WFYJWK>-bChQ4a3th37GmSi! z)4Fw$l?ZOm@mY?b5!>XyA-pA=G227W>BlTcA0otQ@LiL(@x;^>Umi+jxBpJ-MsQAh za4XF9OE;2KPg~I5u!yrv68sWf`bBrGSM05HK>E(RTpNA=lO>2Nd3}EFi@HDX1j4HM z*+b(_Lk%sha_b=)IBhBT>Q3E0KJ1!>+6)yH@hvPYu&Mw0)WegiSz_1+5xfNjs`~o! zFqflJdf^!+CMAV^ddJTkITZ1tYTARsv2GU=m2m?H`EUEJTY+2aCx7D~#EY~#RF(Ul zxL6KkM75ckoa`;RxkX-lUEkdN^zGYw*wp8sarQz1!t z>0r#L14JzF7F^C#U83?{P+>5r!=XZ8pu+^?S97x{41>`9w3?~&25}i=VICYDpfY{! zO6o~vkV$-w51=>X=IIF}OJni6Q-W={=M0zUBCNXau~%>3Hs$i@rYoFJu8EkGu2rg&e77yJ|Z;O)4hnzuUa-*`~>D?Z20%^X|Ld znpJ*3ONdRwPMS=F>f2KHs(jrrpXHC20qfo}!bUz}6b2EhRC|=OC1ImC*0=s`xM%;r zw!S-@%f5eK6HP0ll#x(LLfMp%Et?YABgvjg14Sg1J&F(&nHfov9TF-jlD(sdtl#3mCGn@W|hNrbzcTLU3FB{nL`_rdHhBo`k;J!c#8I7DrP&?oJmh zd(r90O>ZAt-h;Jf*9NbxasKLXIh?Axvva-q5lM9(I=XJf7}n_9j+`gl6*emAT3Vf0 z<78OV_wIp<#d>+|5NR%p*lJzf@sV+^wvpql!cQYMlyxW@h1a{3?QdyJZ5-QIWo6C& zz1A-Ge4#;1czmM`m#n<;o@Y1AX`i=QRoUj#sx;JP;SV@{v6rI@x;^FneJST=MRy^e zTFe%w(qXU5-hQK@QnOOa3!Xy<{1v-~Jgx5@`1183>l3b79anlbt(I*`AHkD z*0Q9*AUPmf?4U7pt7x+{4VBNid}nj)Q7QM z)oh`_6HcVJDGBTuY7lcdPBrJ|riG6h3*wmh>L9#kA$AY^I5xz_DzJ$w1lBjyonHxI zX}^CjFTXrg<=~Uy?eq06J~mQEXQ;R9kVXe zr}paXV0bSkD66U$qirN-6xG@OVJ4h8PFPIDx)Z3~i4$v}6H0sj{QccgI~yCtvgz%R zEn)iOz=0G5!ws7mi6hEWp7F(b z&upZ`!><41$-HH~&F-CCtYLk(6`r5r6P}&>;Ur$~URLi^MqOjZDseBkrB>U_&ufaZah<5f>VrN;N{IQG&NVdsS#20!Xu=Fu*N^Wi4J{L?!3 zv5uEje`8*jR!nTQqNxAIg00&l`i@N>bs_!O*E zzvmt9UbegJTl2${H|rW3`=GBRco&P170?GFaUg0hNa>)Y!T>|<+pQL{i4q>_#lqM& z^S-lF$~llD(Y+H#d)_ zMzheC4L20xu^Wi`bN9!<^#$9urgwf?d@m&)%%x<0hu2cV*Xod?)@==$^w3jbA!23s zq*boUFiH(DcVDpGsPx7z_d#rC71JT;cdlPw>+S6gD2E4_&7U5>WLU2YbM(Qm7~dN0 zGNiE>RrtAZnJg~Htqu+j#-1(6o-oVs@Rtp0X5PcB+cG*XU$}4~_36`))4X!Xawrio zW&%3tW(sdzp1sZK(TSqd$x~; z>U~E?KvtGqDx1-#YsV?%HDknF(nr33*!EE{N^Kz5aQ~m;=So_CU%u2_yg0sUAbF?p zzSE>`N^O_qLYzhT&&8$w!2u5cW2R+BH~Y+I2WCH?)9D}TeST(3qGZ`OX>Q3wN)}?0 z_beC_Bm>sk@;!U2IEPmx*l7eXbe3+^}woCtrapPm{Q!^*P6tH%z6kqckXf+$(v>pzr=b?Rbt~?*@m8(crcsCbmeEuDJAE&l*X_ z9`T~$qYP9uu*lngQ02l4yTO5>@*F0Uo~o%L`&#iP7ftgWci!Int=9G>I9KD`>eIso z(srDg&zFLqJU^SjHge&|OhbT?apCaO7n}dqW+#4Nv1DubWApOXPuH%GLPs@DojiF% zllFWmqFS6@JW8Lo?pmGScI(*|Ih3iXw_e-u-v71p%#YTB++BB8;<9WX?-%**qIC0^ zeR}%tQDL6eP_&0#|mfrctL$WJBU4Si_y*IqPT`iNJK60~jYQA79SM(G9_{Q{e zP4BV~aI>o1jUQ0xSii08et0X5k|9Ui1lPf~fD}8|KR!@V+Y2VR|GlYTYs>a1JN;gB zw`pKG$D7t-FXzK~@uQxUF{<2K6Kmu+ueHVQks7l!ZI;v5XF_{L2JJ78YA3&$z{))D zP&$#JH)I+=VxJ1{!GkbfUwiGfSf>P7z-S}qdfS#Jiy>v1?0m6LKvOfegsEF}E!o0$ zb*lj%quoK3x_W1!(d~VXpC2dd23B*L=AU_Pcu1$YE~6;R-%*Ed`(aIDzA#g+Qr4p2&AwoJGaO4`k~n$W_aV?+l~`T0c(Z=ld$(zN(3 zfAX6i&u`gzyHUG~;;Bi+6~4dH3vu1n<{m}3ow1w>2iDs8JXx#t)@FiD zqPvfEfBpK_hW&b*Y2UZqr#=LB){NcV61x6Yb!1&|L-J}KlTt)ovvaG|cS}dn;UhfLj&O8sIgRy*py&1A61V;z?vMB68$yEXgJ$z;Qwvk(hF00Iy1bl^8^VNrE zz}_=G)Bbtue_8-I^5JTUT_#zd=!yNMB|m}>e#Vpo&KlVu1*|*d8}EIZcY$; zIrrs)eOFQFDK_7G`X;{j$+QFR{miri{g=;6Sy>NxM@LrMox&ntrrYf-UzfVR>WIDG z=F$2ESrz4|>=hk0x#W%-LUM~5Loh3vlZ1i2AtZ+h5bVhFpJYF8=cGzKnAptYYRHJjmwmAAP7aSaF zCfuk7$kxxFMMs=83X=0X|3JL%_~lpD zs#;M?{ak9r9K2JKep!p(HwPQPY~qw_EFt?irR>Q$BgH$fdR!~*S4XO4l3s+ios+;I zjz~`8{;wWCri;<}W2vV4Ul9=a8NU}$13+Z>;m*|M*x~u+kJ;TT_C@h`UL}uRdB>p~ z8MZsu;JSg4dSi`5;^{w~!P}l>Df2y4o$qcJa^&gJ&{{bEs@HXnN8(uI_OKv2#q>BG z!4+{a6Rla^mX1leGk4h(dRp9M(r*Z1e7b~+nS}+D)ZvO;sJ;}c@xa~{#QU(*yf#Dk zat3=7z*)h%=M+e1C{Gb~sO95kxoF~>+#42Q$Bwg%xr<}*TVHgk@iFd=RW-}+tP{^% z6z?9iXg(CKh!hD{VqFcLDt-(IImofb0cCBi)`>K6LG9Zg-oAa?f5{))7f=;~7{~rb zfL?*0#-!2`8wXM_T7bp5sOS~Qd;nCk7;Qey`xetJp9^dwAO0EswGc8RiSjk>y)A~D zQ_pyEhM$M|5k3a2IdLw*6m$ygQ^-;8ucO^NHjP1M9E>_e?-Qn=RZx7zElc^#(QMg> zpN`D;K>;FmWl1z%N zYvlLA!LQ2T`MTmD{ zO;&J!V!2(at9vw1VD|9=35i|I2Mx|?X_pOC#5YwEL7lTS=O{>Kgxd?(a)|Cnu-4rlqAN?Z5k! zKJ3@Byu%Rg$+3Rh&2=}c;LEO*pHYz%+$|!d+_A&txBK5YXuaP2a^NPvB1Uy@CSM9}|!(N&U)wlL&UmU5ua;$aCqAI+v zCwZ7PMhV`>+}unQaJU)rTd@W!Mo>5s6Q}WktPFqchSARf9P?cQ)IoJA{0 z?3bX{Ui%%gdLcpP9{e1a5@9Xzfghipg|bB=WEqSp`EXMDAnD;Spld@mf>Ic$-zUdF z0*9d=`=MX)cL#z`2KK@#a$V2La&>buwquwhjISLK3tsB|-ok#z&2FZo| z37~;t3AMbYKVKs!4Hek$P~X}B38;&)Nqz{4EudI)McYsfpdI?XN}~w`ysx&i>I}Or1LEhm6DQV*PhIjep%;E^34~ zWKHDc34>X4w!L9AhL03LC8wXpnNBKjg53p)M&|xVNPi=@n26f>C-pH;mrN zc4%2DIAEZ%qqozvi&GGcO>p#$H9p@l^s5yZ5ArZhq5>PYOG6~2y7qOKDh-_9h3`GJ zh2?o~Z?Dpt;D!UGPpSkoUQlB_fT&n?w%4I(e_K zO`cWC`yRd?#1PmU)75q19ZR}6>d+MJ1O5`M+foIdwvAZ8@gFMP=U|3T`e;RP;kPK5 zm?US)+u}8yc_GWofJJOj@x;c)!o#2Jj;cB_(yE7zpg6z?ab&^`s!n2ZUtAo6UnVFi zsmjZclA5~CFe-Fh`ALTzZ*uDGc;Cp|Xr$!}o z*a|5o^6aNws~O@=L5$w< z{}n&1t*K#~uki<7<~HrEzRjfZc*Rc|FSXi}CPZ1=Df0t*Puye!82+yW*h{~IGLc_c z4b7ISl9*rCjK1HPX7>ILOLC_#we6EY>!c8N_S-=H4i5eU2>Lg^E{4SJyPxV4jd=Wb0<~ z>XJ3-%$!l}ksTXk+kpAA=xQ@Grs_Lsk#YUjp9g-V@MQ)4OLIeIh(@7Fx%bfu%qU}V zn23mnY=#Tgzr$=V^b4VcqNCeTM{8=*ihOgW|8&k{H-Q7@ zx-Jb7-%|D57g$a<^XnH8i(ja_unbYEMSa9Rh?^)JA!uLeSzYw|>R7cE+Fn3oby)R{ zEsjL9WSDQY=HJo}v+7L%7sRVpkRZ?Um-N%Yx7!mtaObh=qXv_+)kNwp`|vKu!E}j8 zF_*#T7WH(;9KI^z5(!BL=oVieOeFQqDhO4hh_Vsebbpc&e4=Ouu@CRZH)L=htCOF1 z+CysZ_Z{{153=|%Omc<_T4N$$^Ie>C!kd%iHOTVM;J#;_8g958FzQ zJS<028h4inPI(TiLq5jM`BqKP(Ot!FN9**wYXaAc5(&o{4a`3_54q_MZq_u04Az)$ z;>08z1wsacVHDAalNISZcI;5tpAhd*8U(ilEH39py-tCLB=qs=GR_TGtZGDf_z68d zMuaYf!pR>$NTazWZ536xCa8to9$!!JGOVrHlzB{8_^?%3k+FeVpGvlhK z($kYdW8?076}f9O`DxJbv0Ug%*Sb1u6lR+;!JTg@F8W^+;V2r?H37cd6qvMKZ?3@> zFvxt30K-l)81jTGGIzYy)!nyu?+s7MJ7^(dRewx-sv8DBka%n%7|Z=2;R{oAqdYGI zr;t#aW4||Dqg+aB#@U!>wmX5czv#NC!xSdR33UU;6E%ZtLB&LPd5c@y{qL6_XTO@2 z_51g43iA)=pu55ufiPOt&Fv_(^Z@JjWr$uy*(PXSvjt(W8B27qD##MLZLA_eGv8`+ z5A4MES?^dFhlLl+;B9h2;7ZmiWTy*)#0{1~7TDJ2>gpLQe|wOJ!wcd~jMy%A4o#^Bf)>ePdTB2+<)0+R|7F4}Jjtm_|m6SSR1CvBG z6UgoYF`9Y?{mm+}+IRY~w!Z$&CM$_*c)X)wjDV2i{EM#oh6W&-AfCzJ3^+gJ-Gs{r z0zwz$-&=<-(`xhko|~h?3yDMGF5stT%M*=V60iF!T*Vg+N>P!k`vJIGzp6v;fiz@7#wQ z@c6L4o}L2m0f-spHMiUx1Vh%;v`vu6HxadsrWECtOA)zDSe)7nF z*G*xa`ooF8$ZZioIDJv;(AVE7sS67Ww!X@TEV5E1j+8%pbeR@E9G;H z*ra|ud#So_`m~M?F;l}4!=ZlE8Nn3*#^vj#{93@lOv z&Yk@pKhk$8VU@V&$Qb671hy_~tuo>yTvUt&b;!8Df zZB$kRxR!s49MXV7?jsvzN!r`H@$rpq8Zu4R*cm0PW{w6F=5qhma1m#mZ{I>cjUA1m zrs5!)9`xt9Wl$3JQhyqa{jb(q%h4!^$OOss%zR(iFJj1yGXjv#0IeX{0ZZ`HR2+}9 zvoZ8}jmW=9cN)mp?|Cz`=R5RWP@n*!HcvE=+{1JDa2Pp9DDy%A34wI zmzX%AK#jpX;PIQ|1JWM$%Nx!cIFDaNF+gEn&jtz-FdHgU1m23( z#fl+(KNi4mKVS&*0J31zD65BYel@PlL@vSbMFOD#=N53+i0cpM5(4=X%mk|%QGW

C@-!G~>`HCOz<)K*Puu#b}8Z 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 isr-Nc>iaOWx< $out diff --git a/builddefs/config.bzl b/builddefs/config.bzl new file mode 100644 index 00000000..8192dcac --- /dev/null +++ b/builddefs/config.bzl @@ -0,0 +1,62 @@ +# Description: +# Common configuration options. + +COMMON_JS_SUPPRESSIONS = [ + # TODO(benvanik): somehow make all our code typed correctly ;) + "JSC_UNKNOWN_EXPR_TYPE", + + # TODO(benvanik): go replace naked Object with !Object or ?Object. + "JSC_IMPLICITLY_NULLABLE_JSDOC", + + # goog.result deprecated in favor of goog.Promise. + "JSC_DEPRECATED_CLASS_REASON", + "JSC_DEPRECATED_PROP_REASON", +] + +SHARED_CSS_FLAGS = [ + "--allow-unrecognized-functions", +] + select({ + "//:dbg": [ + "--preserve-comments", + "--pretty-print", + ], + "//:fastbuild": [ + "--preserve-comments", + "--pretty-print", + ], + "//:opt": [ + #"--css-renaming-prefix=wtf_", + ], +}) + +SHARED_JS_FLAGS = [ + "--summary_detail_level=3", + "--create_source_map=%outname%.map", + "--source_map_format=V3", + "--define=goog.soy.REQUIRE_STRICT_AUTOESCAPE=false", +] + select({ + "//:dbg": [ + "--compilation_level=WHITESPACE_ONLY", + "--define=goog.DEBUG=true", + "--define=goog.asserts.ENABLE_ASSERTS=true", + ], + "//:fastbuild": [ + "--compilation_level=SIMPLE", + "--define=goog.DEBUG=true", + "--define=goog.asserts.ENABLE_ASSERTS=true", + ], + "//:opt": [ + "--compilation_level=ADVANCED", + "--use_types_for_optimization", + #"--collapse_variable_declarations", + #"--collapse_anonymous_functions", + #"--collapse_properties", + #"--disambiguate_properties", + # rewrites things to be smaller but likely not better + # http://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/javascript/jscomp/FunctionRewriter.java + #"--rewrite_function_expressions=false", + # slow - may want disabled + #"--devirtualize_prototype_methods", + #"--devirtualize_prototype_methods=false", + ], +}) diff --git a/builddefs/embed_files.py b/builddefs/embed_files.py new file mode 100644 index 00000000..69afdddb --- /dev/null +++ b/builddefs/embed_files.py @@ -0,0 +1,36 @@ +import base64 +import os +import sys + +def main(argv): + encoding = argv[1] + template_path = argv[2] + out_path = argv[3] + src_path = argv[4] + + with open(src_path, "r") as file: + src_str = file.read() + + if encoding == 'base64': + src_str = base64.b64encode(src_str) + else: + src_str = src_str.replace('\n', '\\n') + src_str = src_str.replace('\'', '\\\'') + + sanitized_src_path = src_path + sanitized_src_str = src_str + + with open(template_path, "r") as file: + template_str = file.read() + + out_str = template_str + out_str = out_str.replace('%output%', sanitized_src_str) + out_str = out_str.replace('%path%', sanitized_src_path) + + with open(out_path, "w") as file: + file.write(out_str) + + return 0 + +if __name__ == "__main__": + sys.exit(main(sys.argv)) diff --git a/builddefs/file_rules.bzl b/builddefs/file_rules.bzl new file mode 100644 index 00000000..85fd0cc9 --- /dev/null +++ b/builddefs/file_rules.bzl @@ -0,0 +1,118 @@ +# Description: +# Rules for file manipulation. + + +def _concat_files_impl(ctx): + ctx.actions.run( + inputs = ctx.files.parts, + outputs = [ctx.outputs.out], + arguments = [ + ctx.outputs.out.path, + ] + [f.path for f in ctx.files.parts], + progress_message = "Concating into %s" % (ctx.outputs.out.short_path), + executable = ctx.executable._concat_tool, + ) + +concat_files = rule( + implementation = _concat_files_impl, + attrs = { + "parts": attr.label_list(allow_files = True), + "out": attr.output(mandatory = True), + "_concat_tool": attr.label( + executable = True, + cfg = "host", + allow_files = True, + default = Label("//builddefs:concat_files"), + ), + }, +) + + +def _embed_files_impl(ctx): + # Write template string to a file as that's what expand_template wants. + template_file = ctx.actions.declare_file(ctx.label.name + "_template") + ctx.actions.write( + output = template_file, + content = ctx.attr.wrapper, + ) + + # Convert and template each file. + templated_src_files = [] + for src_file in ctx.files.srcs: + # Prepare temporary output file. + templated_src_file = ctx.actions.declare_file(src_file.short_path + ".templated") + templated_src_files.append(templated_src_file) + + # Issue conversion command. + ctx.actions.run( + inputs = [template_file, src_file], + outputs = [templated_src_file], + arguments = [ + ctx.attr.encoding, + template_file.path, + templated_src_file.path, + src_file.path, + ], + progress_message = "Embedding %s" % (src_file.short_path), + executable = ctx.executable._embed_tool, + ) + + # Concat all files together. + ctx.actions.run( + inputs = templated_src_files, + outputs = [ctx.outputs.out], + arguments = [ + ctx.outputs.out.path, + ] + [f.path for f in templated_src_files], + progress_message = "Concating into %s" % (ctx.outputs.out.short_path), + executable = ctx.executable._concat_tool, + ) + +embed_files = rule( + implementation = _embed_files_impl, + attrs = { + "srcs": attr.label_list(allow_files = True), + "wrapper": attr.string(default = "%output%"), + "encoding": attr.string(default = "utf8"), + "out": attr.output(mandatory = True), + "_embed_tool": attr.label( + executable = True, + cfg = "host", + allow_files = True, + default = Label("//builddefs:embed_files"), + ), + "_concat_tool": attr.label( + executable = True, + cfg = "host", + allow_files = True, + default = Label("//builddefs:concat_files"), + ), + }, +) + + +def _strip_comments_impl(ctx): + ctx.actions.run( + inputs = ctx.files.srcs, + outputs = [ctx.outputs.out], + arguments = [ + ctx.files.srcs[0].path, + ctx.outputs.out.path, + ], + progress_message = "Stripping comments from %s" % (ctx.outputs.out.short_path), + executable = ctx.executable._strip_comments_tool, + ) + +strip_comments = rule( + implementation = _strip_comments_impl, + attrs = { + "srcs": attr.label_list(allow_files = True), + "out": attr.output(mandatory = True), + "_strip_comments_tool": attr.label( + executable = True, + cfg = "host", + allow_files = True, + default = Label("//builddefs:strip_comments"), + ), + }, +) diff --git a/builddefs/less_rules.bzl b/builddefs/less_rules.bzl new file mode 100644 index 00000000..051e7eee --- /dev/null +++ b/builddefs/less_rules.bzl @@ -0,0 +1,93 @@ +# Description: +# Rules for building LESS CSS libraries. + +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_css_library") + + +def _unfurl(deps, provider=""): + """Returns deps as well as deps exported by parent rules.""" + res = [] + for dep in deps: + if not provider or hasattr(dep, provider): + res.append(dep) + if hasattr(dep, "exports"): + for edep in dep.exports: + if not provider or hasattr(edep, provider): + res.append(edep) + return res + + +def _collect_less_deps(deps): + """Aggregates transitive LESS source files from unfurled deps.""" + srcs = [] + for dep in deps: + srcs += getattr(dep.less_css_library, "srcs", []) + srcs += getattr(dep.less_css_library, "main_srcs", []) + srcs += getattr(dep.less_css_library, "transitive_srcs", []) + return srcs + + +def _less_css_library(ctx): + deps = _unfurl(ctx.attr.deps, provider="less_css_library") + less_deps = _collect_less_deps(deps) + less_deps.extend(ctx.files.srcs) + + common_less_args = [] + include_paths = ["."] + if ctx.attr.includes: + include_paths.extend(ctx.attr.includes) + common_less_args.append("--include-path=" + ":".join(include_paths)) + if ctx.attr.defs: + common_less_args.extend(ctx.attr.defs) + + # Translate each .less into a .css individually. + css_files = [] + for src in ctx.files.main_srcs: + css_file = ctx.actions.declare_file(src.short_path[:-5] + ".css") + css_files.append(css_file) + ctx.actions.run( + outputs = [css_file], + inputs = [src] + less_deps, + executable = ctx.executable._compiler, + arguments = common_less_args + [ + src.path, + css_file.path, + ], + mnemonic = "LessCompile", + progress_message = "Translating LESS to CSS: %s" % (src.short_path), + ) + + return struct( + files = depset(css_files), + less_css_library = struct( + srcs = ctx.files.srcs, + main_srcs = ctx.files.main_srcs, + transitive_srcs = less_deps, + ), + closure_css_library = struct( + srcs = css_files, + deps = deps, + data = ctx.attr.srcs, + orientation = ctx.attr.orientation, + ), + ) + +less_css_library = rule( + implementation = _less_css_library, + attrs = { + "main_srcs": attr.label_list(allow_files=FileType([".css", ".less"])), + "srcs": attr.label_list(allow_files=FileType([".css", ".less"])), + "deps": attr.label_list(providers=["less_css_library"]), + "includes": attr.string_list(), + "defs": attr.string_list(), + "orientation": attr.string(default="LTR"), + "_compiler": attr.label( + default = Label("//builddefs:lessc"), + allow_files = True, + cfg = "host", + executable = True, + ), + }, + #outputs = {"out": "%{name}.css"}, +) + diff --git a/builddefs/packaging_rules.bzl b/builddefs/packaging_rules.bzl new file mode 100644 index 00000000..9cde56e9 --- /dev/null +++ b/builddefs/packaging_rules.bzl @@ -0,0 +1,66 @@ +# Description: +# Rules for packaging build outputs. + + +def _pkg_zip_impl(ctx): + # Copy all files into the staging directory. + staging_dir = ctx.attr.name + "_pkg" + staged_files = [] + for src_file in ctx.files.srcs: + # Remap path, if desired. + original_path = src_file.short_path + staged_path = original_path + for (prefix, replacement) in ctx.attr.path_mapping.items(): + if staged_path.startswith(prefix): + staged_path = staged_path.replace(prefix, replacement) + staged_path = "/".join([staging_dir, staged_path]) + + # Copy to staging path. + staged_file = ctx.actions.declare_file(staged_path) + staged_files.append(staged_file) + ctx.actions.run_shell( + inputs = [src_file], + outputs = [staged_file], + command = "cp $1 $2", + arguments = [ + src_file.path, + staged_file.path, + ], + progress_message = "Staging %s" % (src_file.short_path), + ) + + # Zip all of the files in the staging dir. + staged_file_paths = [] + staged_base_path = "/".join([ctx.label.package, staging_dir]) + "/" + for staged_file in staged_files: + relative_path = staged_file.short_path.replace(staged_base_path, "") + staged_file_paths.append("/".join([relative_path])) + ctx.actions.run_shell( + inputs = staged_files, + outputs = [ctx.outputs.out], + command = "\n".join([ + "ROOT_PATH=`pwd`", + "cd %s" % ("/".join([ctx.outputs.out.dirname, staging_dir])), + "zip -rqD $ROOT_PATH/%s %s" % ( + ctx.outputs.out.path, + " ".join(staged_file_paths), + ), + "cd -", + ]), + progress_message = "Zipping package %s" % (ctx.outputs.out.short_path), + ) + + return struct( + files = depset([ctx.outputs.out]), + ) + +pkg_zip = rule( + implementation = _pkg_zip_impl, + attrs = { + "srcs": attr.label_list(allow_files = True), + "path_mapping": attr.string_dict(), + }, + outputs = { + "out": "%{name}.zip", + }, +) diff --git a/builddefs/strip_comments.py b/builddefs/strip_comments.py new file mode 100644 index 00000000..7fe1e9a0 --- /dev/null +++ b/builddefs/strip_comments.py @@ -0,0 +1,30 @@ +import os +import re +import sys + +def main(argv): + src_path = argv[1] + out_path = argv[2] + + with open(src_path, "r") as file: + src_str = file.read() + + # Code from Markus Jarderot, posted to stackoverflow + def replacer(match): + s = match.group(0) + if s.startswith('/'): + return "" + else: + return s + pattern = re.compile( + r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"', + re.DOTALL | re.MULTILINE) + out_str = re.sub(pattern, replacer, src_str) + + with open(out_path, "w") as file: + file.write(out_str) + + return 0 + +if __name__ == "__main__": + sys.exit(main(sys.argv)) diff --git a/extensions/wtf-injector-chrome/BUILD b/extensions/wtf-injector-chrome/BUILD new file mode 100644 index 00000000..984c4946 --- /dev/null +++ b/extensions/wtf-injector-chrome/BUILD @@ -0,0 +1,64 @@ +# Description: +# WTF Chrome Extension. + +package(default_visibility = ["//:internal"]) + +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_css_binary") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_binary") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_deps") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library") +load("//builddefs:config.bzl", "SHARED_CSS_FLAGS") +load("//builddefs:config.bzl", "SHARED_JS_FLAGS") +load("//builddefs:file_rules.bzl", "concat_files", "embed_files", "strip_comments") +load("//builddefs:less_rules.bzl", "less_css_library") +load("//builddefs:packaging_rules.bzl", "pkg_zip") + +# CWS hates comments in extension manifests - strip them all. +strip_comments( + name = "stripped_manifest", + srcs = ["manifest.template.json"], + out = "manifest.json", +) + +less_css_library( + name = "popup_styles", + includes = ["src"], + main_srcs = ["popup.less"], + deps = [ + "//src/wtf/ui/styles", + ], +) + +filegroup( + name = "extension_files", + srcs = [ + "//app", + "//bindings/js", + ":stripped_manifest", + ":popup_styles", + "background.js", + "debugger.js", + "extension.js", + "injectedtab.js", + "options.js", + "popup.html", + "popup.js", + "tracer.js", + "uri.js", + "wtf-call-tracing.js", + "wtf-injector.js", + "wtf-process.js", + ] + glob([ + "assets/icons/*.png", + "third_party/*.js", + ]), +) + +pkg_zip( + name = "wtf-injector-chrome", + srcs = [":extension_files"], + path_mapping = { + "bindings/js/": "", + "extensions/wtf-injector-chrome/": "", + }, +) diff --git a/extensions/wtf-injector-chrome/BUILD.anvil b/extensions/wtf-injector-chrome/BUILD.anvil deleted file mode 100644 index d62be660..00000000 --- a/extensions/wtf-injector-chrome/BUILD.anvil +++ /dev/null @@ -1,81 +0,0 @@ -# Copyright 2012 Google Inc. All Rights Reserved. - -__author__ = 'benvanik@google.com (Ben Vanik)' - - -# Chrome injector extension release output - - -# ------------------------------------------------------------------------------ -# Extension -# ------------------------------------------------------------------------------ - -# CWS hates comments in extension manifests - strip them all -strip_comments( - name='stripped_manifest', - srcs=['manifest.json']) - -# Files relating to the injected tracing code -file_set( - name='tracing_files', - srcs=[ - '../..:wtf_trace_web_js_compiled', - ]) - -file_set( - name='all_less', - srcs=glob('../../src/**/*.less')) -less_css_library( - name='popup_styles', - srcs=['popup.less', ':all_less'], - include_paths=['src',], - compiler_flags=[ - #'--compress' - ]) - -file_set( - name='extension_files', - srcs=[ - ':stripped_manifest', - ':tracing_files', - ':popup_styles', - 'background.js', - 'debugger.js', - 'extension.js', - 'injectedtab.js', - 'options.js', - 'popup.html', - 'popup.js', - 'tracer.js', - 'uri.js', - 'wtf-call-tracing.js', - 'wtf-injector.js', - 'wtf-process.js', - ] + - glob('assets/scripts/*.js') + - glob('assets/icons/*.png') + - glob('third_party/*.js'), - src_exclude_filter='*-deps.js') - -file_set( - name='app_files', - srcs=['../../app:app_files'], - src_exclude_filter='manifest.json') - -file_set( - name='combined_files', - srcs=[ - ':extension_files', - ':app_files' - ]) - -copy_files( - name='deploy', - srcs=[':combined_files'], - out='../../wtf-injector-chrome/', - flatten_paths=['extensions/wtf-injector-chrome/']) - -archive_files( - name='wtf-injector-chrome', - srcs=':combined_files', - flatten_paths=['extensions/wtf-injector-chrome/']) diff --git a/extensions/wtf-injector-chrome/injectedtab.js b/extensions/wtf-injector-chrome/injectedtab.js index d78bc457..358b6711 100644 --- a/extensions/wtf-injector-chrome/injectedtab.js +++ b/extensions/wtf-injector-chrome/injectedtab.js @@ -69,7 +69,9 @@ var InjectedTab = function(extension, tab, pageStatus, pageOptions, port) { */ this.debugger_ = null; - if (pageStatus == PageStatus.WHITELISTED && + // Chrome debugger support is disabled until it's updated. + if (false && + pageStatus == PageStatus.WHITELISTED && pageOptions['wtf.trace.provider.chromeDebug'] !== false) { var timelineEnabled = pageOptions['wtf.trace.provider.chromeDebug.timeline'] !== false; diff --git a/extensions/wtf-injector-chrome/manifest.json b/extensions/wtf-injector-chrome/manifest.template.json similarity index 99% rename from extensions/wtf-injector-chrome/manifest.json rename to extensions/wtf-injector-chrome/manifest.template.json index dd64f68a..028703aa 100644 --- a/extensions/wtf-injector-chrome/manifest.json +++ b/extensions/wtf-injector-chrome/manifest.template.json @@ -4,7 +4,7 @@ // These can be copy/pasted directly between them. // --------------------------------------------------------------------------- "manifest_version": 2, - "version": "2015.7.15.1", + "version": "2017.10.19.1", "minimum_chrome_version": "23", "homepage_url": "https://github.com/google/tracing-framework", diff --git a/extensions/wtf-injector-chrome/popup.html b/extensions/wtf-injector-chrome/popup.html index 113d78dd..6de7214b 100644 --- a/extensions/wtf-injector-chrome/popup.html +++ b/extensions/wtf-injector-chrome/popup.html @@ -2,7 +2,7 @@ - + diff --git a/extensions/wtf-injector-firefox/package.json b/extensions/wtf-injector-firefox/package.json index 52bc6043..9e8d7270 100644 --- a/extensions/wtf-injector-firefox/package.json +++ b/extensions/wtf-injector-firefox/package.json @@ -2,7 +2,7 @@ "name": "web-tracing-framework", "fullName": "Web Tracing Framework", "id": "jid1-AdSGhlstK2V5ig", - "version": "2015.7.15.1", + "version": "2017.10.19.1", "description": "Rich tools for instrumenting, analyzing, and visualizing web apps. Make your app jank-free at 60fps!", "author": "benvanik@google.com", "license": "BSD" diff --git a/externs/BUILD b/externs/BUILD new file mode 100644 index 00000000..fb746320 --- /dev/null +++ b/externs/BUILD @@ -0,0 +1,16 @@ +# Description: +# Externs for browser/node/etc APIs. + +package(default_visibility = ["//:internal"]) + +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library") + +closure_js_library( + name = "externs", + srcs = [ + "analytics.js", + "chrome.js", + "drive.js", + "node.js", + ], +) diff --git a/externs/chrome.js b/externs/chrome.js index 13dc98c6..98dfc5fe 100644 --- a/externs/chrome.js +++ b/externs/chrome.js @@ -13,14 +13,6 @@ */ -/** - * @typedef {{ - * addListener: function(!Function), - * removeListener: function(!Function) - * }} - */ -var ChromeEvent; - // var chrome; @@ -61,6 +53,10 @@ chrome.app.runtime.onLaunched; var ChromeAppWindow; +/** @type {!ChromeAppWindow} */ +chrome.app.window; + + /** * @param {string} url * @param {!Object} options diff --git a/externs/node.js b/externs/node.js index 3c224bdd..5ee02406 100644 --- a/externs/node.js +++ b/externs/node.js @@ -112,7 +112,7 @@ Buffer.byteLength = function(value) {}; /** - * @return {string} + * @override */ Buffer.prototype.toString = function() {}; diff --git a/lint.sh b/lint.sh new file mode 100755 index 00000000..dfa909ea --- /dev/null +++ b/lint.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +find -name 'BUILD' -print0 | xargs -0 buildifier diff --git a/package.json b/package.json index b50aecde..062df44a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tracing-framework", - "version": "2015.7.15-1", + "version": "2017.10.19-1", "description": "Web Tracing Framework instrumentation and analysis library.", "keywords": [ "javascript", @@ -20,23 +20,23 @@ }, "scripts": {}, "dependencies": { - "falafel": "0.1.4", - "mkdirp": "0.3.5", - "optimist": "0.3.5", - "ws": "0.4.25", - "string-hash": "~1.1.0", - "temporary": "0.0.7" + "falafel": "2.1.0", + "mkdirp": "0.5.1", + "optimist": "0.6.1", + "ws": "3.2.0", + "string-hash": "~1.1.3", + "temporary": "0.0.8" }, "devDependencies": { - "benchmark": "1.0.0", - "chai": "1.2.0", - "less": "1.4.2", - "mocha": "1.4.2", - "pegjs": "~0.7.0", - "wd": "0.0.32" + "benchmark": "2.1.4", + "chai": "4.1.2", + "less": "2.7.2", + "mocha": "3.5.3", + "pegjs": "~0.10.0", + "wd": "1.4.1" }, "optionalDependencies": { - "microtime": "2.1.2" + "microtime": "2.1.6" }, "files": [ "build-out/wtf_node_js_compiled.js", diff --git a/scripts/prepare-publish.sh b/scripts/prepare-publish.sh index 2023745d..99442edf 100755 --- a/scripts/prepare-publish.sh +++ b/scripts/prepare-publish.sh @@ -15,12 +15,22 @@ if [ ! -d ".git" ]; then exit 1 fi +GIT_USERNAME=`git config user.name` +GIT_USEREMAIL=`git config user.email` + # ============================================================================= # Build everything # ============================================================================= echo "Building everything..." -./third_party/anvil-build/anvil-local.sh build :fast :debug :release +bazel build -c opt \ + //app \ + //app:wtf-app \ + //bindings/js \ + //bindings/js:wtf_trace_min_js_compiled \ + //bindings/js:wtf_node_js_compiled \ + //bindings/js:wtf-trace-web-api \ + //extensions/wtf-injector-chrome echo "" # ============================================================================= @@ -28,24 +38,19 @@ echo "" # ============================================================================= echo "Building extensions..." -# Chrome. -rm -rf build-bin/wtf-injector-chrome -./third_party/anvil-build/anvil-local.sh deploy -o build-bin/ extensions/wtf-injector-chrome:deploy -./third_party/anvil-build/anvil-local.sh deploy -o build-bin/ :injector - # Firefox. -rm -rf build-bin/wtf-injector-firefox -./third_party/anvil-build/anvil-local.sh deploy -o build-bin/ extensions/wtf-injector-firefox:deploy -cd third_party/firefox-addon-sdk/ -source bin/activate -cd ../.. -cd build-bin/wtf-injector-firefox/ -cfx xpi \ - --update-link https://tracing-framework.appspot.com/CURRENT/web-tracing-framework.xpi \ - --update-url https://tracing-framework.appspot.com/CURRENT/web-tracing-framework.update.rdf -cd ../.. -mv build-bin/wtf-injector-firefox/web-tracing-framework.xpi build-bin/extensions/ -mv build-bin/wtf-injector-firefox/web-tracing-framework.update.rdf build-bin/extensions/ +# rm -rf build-bin/wtf-injector-firefox +# ./third_party/anvil-build/anvil-local.sh deploy -o build-bin/ extensions/wtf-injector-firefox:deploy +# cd third_party/firefox-addon-sdk/ +# source bin/activate +# cd ../.. +# cd build-bin/wtf-injector-firefox/ +# cfx xpi \ +# --update-link https://tracing-framework.appspot.com/CURRENT/web-tracing-framework.xpi \ +# --update-url https://tracing-framework.appspot.com/CURRENT/web-tracing-framework.update.rdf +# cd ../.. +# mv build-bin/wtf-injector-firefox/web-tracing-framework.xpi build-bin/extensions/ +# mv build-bin/wtf-injector-firefox/web-tracing-framework.update.rdf build-bin/extensions/ echo "" # ============================================================================= @@ -53,28 +58,55 @@ echo "" # ============================================================================= echo "Building gh-pages..." -# Clean first. -rm -rf build-bin/gh-pages/ +# Ensure we have a sibling tracking-framework-gh-pages checkout. +SELF_DIR=$PWD +GH_PAGES=$PWD/../tracing-framework-gh-pages +if [ ! -d "$GH_PAGES" ]; then + # Not found - create and clone. + echo "Creating tracing-framework-gh-pages..." + git clone git@github.com:google/tracing-framework.git $GH_PAGES + cd $GH_PAGES + git checkout gh-pages +else + # Reset hard to the current version. + echo "Resetting tracing-framework-gh-pages..." + cd $GH_PAGES + git reset --hard + git pull + git merge origin/gh-pages +fi +cd $SELF_DIR + +# Reset existing bin dir. +if [ -d "$GH_PAGES/bin" ]; then + rm -rf $GH_PAGES/bin/ +fi -./third_party/anvil-build/anvil-local.sh deploy -o build-bin/gh-pages/ :release +# Copy binaries from build results and source tree. +mkdir -p $GH_PAGES/bin +cp bazel-bin/app/wtf-app.zip $GH_PAGES/bin/ +unzip -q bazel-bin/app/wtf-app.zip -d $GH_PAGES/bin/ +cp bazel-bin/bindings/js/wtf-trace-web-api.zip $GH_PAGES/bin/ +cp bazel-bin/bindings/js/wtf_trace_min_js_compiled.js $GH_PAGES/bin/ +cp bazel-bin/bindings/js/wtf_trace_web_js_compiled.js $GH_PAGES/bin/ +cp bazel-bin/bindings/js/wtf_node_js_compiled.js $GH_PAGES/bin/ -# Copy around extension files. -cp build-bin/extensions/web-tracing-framework.* build-bin/gh-pages/extensions/ -rm -rf build-bin/gh-pages/extensions/wtf-injector-firefox/ -mv build-bin/gh-pages/extensions/wtf-injector-chrome/wtf-injector-chrome.zip build-bin/gh-pages/extensions/ -rmdir build-bin/gh-pages/extensions/wtf-injector-chrome/ +# Copy extensions from build results. +mkdir $GH_PAGES/bin/extensions +cp bazel-bin/extensions/wtf-injector-chrome/wtf-injector-chrome.zip $GH_PAGES/bin/extensions/ +# cp bazel-bin/extensions/web-tracing-framework.* $GH_PAGES/extensions/ echo "" # ============================================================================= # Done! # ============================================================================= echo "gh-pages:" -echo " build-bin/gh-pages/" +echo " $GH_PAGES/" echo "Chrome extension:" -echo " build-bin/extensions/wtf-injector-chrome/wtf-injector-chrome.zip" -echo "Firefox extension:" -echo " build-bin/extensions/web-tracing-framework.xpi <-- zip" -echo " build-bin/extensions/web-tracing-framework.update.rdf <-- update RDF" +echo " bazel-bin/extensions/wtf-injector-chrome/wtf-injector-chrome.zip" +# echo "Firefox extension:" +# echo " build-bin/extensions/web-tracing-framework.xpi <-- zip" +# echo " build-bin/extensions/web-tracing-framework.update.rdf <-- update RDF" echo "" echo "Ready for npm publish and ./scripts/update-gh-pages.sh" echo "" diff --git a/scripts/update-third-party.sh b/scripts/publish-gh-pages.sh similarity index 52% rename from scripts/update-third-party.sh rename to scripts/publish-gh-pages.sh index a1654b0f..d827029c 100755 --- a/scripts/update-third-party.sh +++ b/scripts/publish-gh-pages.sh @@ -3,8 +3,11 @@ # Copyright 2012 Google Inc. All Rights Reserved. # This script is only to be used by contributors. -# It will attempt to pull the latest versions of third party code and binaries. -# Before running one should be in a git branch with no pending changes. +# This will build everything, check out the latest tracing-framework gh-pages +# branch, update the bin/ path, and prepare a commit. + +# Break on error. +set -e # This must currently run from the root of the repo # TODO(benvanik): make this runnable from anywhere (find git directory?) @@ -13,33 +16,35 @@ if [ ! -d ".git" ]; then exit 1 fi +echo "" # ============================================================================= -# Node modules +# Look for sibling path or create # ============================================================================= -npm install +GH_PAGES=$PWD/../tracing-framework-gh-pages +if [ ! -d "$GH_PAGES" ]; then + # Not found. + echo "Run prepare-publish.sh first to create gh-pages repo" + exit 1 +fi +cd $GH_PAGES # ============================================================================= -# Git submodules +# Stage all changes # ============================================================================= -echo "Updating git modules..." - -# TODO(benvanik): update each module -SUBMODULES=( anvil-build closure-compiler closure-library closure-linter closure-stylesheets closure-templates ) -cd third_party -for m in ${SUBMODULES[@]} -do - echo "-> third_party/$m" - cd $m - git checkout master - git pull origin master - git merge origin/master - cd .. -done -cd .. -for m in ${SUBMODULES[@]} -do - git add third_party/$m -done +echo "Staging changes..." + +git add --all bin/ +git commit -m "Updating bin/ to the latest version." echo "" +# ============================================================================= +# Push! +# ============================================================================= +echo "Pushing changes..." + +git push origin gh-pages + +echo "" + +cd - diff --git a/scripts/setup.bat b/scripts/setup.bat deleted file mode 100644 index 25b620e3..00000000 --- a/scripts/setup.bat +++ /dev/null @@ -1,54 +0,0 @@ -@ECHO OFF - -REM Copyright 2012 Google Inc. All Rights Reserved. -REM -REM wtf Windows setup script -REM -REM This script will install all dependencies to the system (that it can). -REM The dependencies are all local. -REM -REM Requires: -REM - Git 1.7.5+ -REM - Python 2.7+ -REM - Python easy_install: http://pypi.python.org/pypi/setuptools -REM - node.js v0.6.10+ (containing npm) - -ECHO. -REM ============================================================================ -REM Check for Python/node/etc -REM ============================================================================ -ECHO Checking for dependencies... - -REM TODO(benvanik): check python/node versions - -ECHO WARNING: you need to make sure you have Python 2.6+ and node 0.8.4+! -ECHO WARNING: attempting to install pip - install it yourself if it fails! - -easy_install pip - -ECHO. -REM ============================================================================ -REM Git submodules -REM ============================================================================ -ECHO Fetching submodules... - -git submodule init -git submodule update - -ECHO. -REM ============================================================================ -REM Node modules -REM ============================================================================ -ECHO Installing node modules... - -npm install - -ECHO. -REM ============================================================================ -REM Anvil init -REM ============================================================================ -ECHO Setting up anvil-build environment... - -third_party\anvil-build\setup-local.bat - -ECHO. diff --git a/scripts/setup.sh b/scripts/setup.sh deleted file mode 100755 index fa71fbc9..00000000 --- a/scripts/setup.sh +++ /dev/null @@ -1,143 +0,0 @@ -#!/bin/bash - -# Copyright 2012 Google Inc. All Rights Reserved. - -# wtf unix setup script - -# This script will install all dependencies. Everything is local, no need to -# run as root. -# -# Requires: -# - All: -# - Git 1.7.5+ -# - Python 2.6+ -# - Python pip -# - node.js v0.8.0+ (containing npm) - -# Ensure running as root (or on Cygwin, where it doesn't matter) -if [ "$(id -u)" -eq 0 ]; then - if [ ! -e "/Cygwin.bat" ]; then - echo "This script should not be run as root!" - echo "Run without sudo!" - exit 1 - fi -fi - -# This must currently run from the root of the repo -# TODO(benvanik): make this runnable from anywhere (find git directory?) -if [ ! -d ".git" ]; then - echo "This script must be run from the root of the repository (the folder containing .git)" - exit 1 -fi - -# ============================================================================== -# Check for Python/node/etc -# ============================================================================== -echo "Checking for dependencies..." - -echo "- Python 2.6+:" -if [ ! -e "$(which python)" ]; then - echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" - echo "! Python not found or not in PATH - at least version 2.6 is required !" - echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" - exit 1 -fi -PYTHON_CHECK=`python -c 'import sys; print(sys.version_info >= (2, 6) and "1" or "0")'` -PYTHON_VERSION=`python -c 'import sys; print(sys.version_info[:])'` -if [ "$PYTHON_CHECK" = "0" ]; then - echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" - echo "! Python is out of date - at least version 2.6 is required !" - echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" - echo "Your version: $PYTHON_VERSION" - exit 1 -fi -echo " path: $(which python)" -echo " version: $PYTHON_VERSION" -echo "" -echo "- Python pip:" -if [ ! -e "$(which pip)" ]; then - echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" - echo "! pip not found or not in PATH !" - echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" - echo "Attemping to install via the system package manager..." - if [ "$(which easy_install)" ]; then - # Anything with easy_install (on OS X by default) - sudo easy_install pip - elif [ "$(which apt-get 2>/dev/null)" ]; then - # Linux (Ubuntu) - sudo apt-get install python-pip - elif [ "$(which port 2>/dev/null)" ]; then - # OS X (MacPorts) - sudo port selfupdate - sudo port install pip - else - echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" - echo "! No supported package manager found! !" - echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" - echo " If on OS X ensure you install MacPorts (port) and run again" - echo " Or, manually install these packages:" - echo " pip" - exit 1 - fi -fi -echo " path: $(which pip)" -echo "" -echo "- node.js 0.8.0+:" -if [ ! -e "$(which node)" ]; then - echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" - echo "! node.js not found or not in PATH - at least version 0.8.0 is required !" - echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" - echo "Grab the latest version from: http://nodejs.org/#download" - exit 1 -fi -NODE_CHECK=`node -e "var v = process.version.split('v')[1].split('.'); console.log(v[0] > 0 || v[1] > 8 || v[2] >= 0)"` -NODE_VERSION=`node -e "console.log(process.version)"` -if [ "$NODE_CHECK" = "false" ]; then - echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" - echo "! node.js is out of date - at least version 0.8.0 is required !" - echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" - echo "Your version: $NODE_VERSION" - echo "Grab the latest version from: http://nodejs.org/#download" - exit 1 -fi -echo " path: $(which node)" -echo " version: $NODE_VERSION" - -echo "" -# ============================================================================== -# Git submodules -# ============================================================================== -echo "Fetching submodules..." - -git submodule init -git submodule update - -# On cygwin disable the filemode setting to prevent all the spurious +x diffs. -# This cannot be set globally as git resets it each repo. -if [ -e "/Cygwin.bat" ]; then - git config core.filemode false - git submodule foreach git config core.filemode false -fi - -# Add an ignored gitignore to closure-linter, as the repo is missing one that -# ignores .pyc files. -echo '.gitignore' > third_party/closure-linter/.gitignore -echo '*.pyc' >> third_party/closure-linter/.gitignore - -echo "" -# ============================================================================= -# Node modules -# ============================================================================= -echo "Installing node modules..." - -npm install - -echo "" -# ============================================================================= -# Anvil init -# ============================================================================= -echo "Setting up anvil-build environment..." - -third_party/anvil-build/setup-local.sh - -echo "" diff --git a/scripts/update-gh-pages.sh b/scripts/update-gh-pages.sh deleted file mode 100755 index 674533cf..00000000 --- a/scripts/update-gh-pages.sh +++ /dev/null @@ -1,88 +0,0 @@ -#!/bin/bash - -# Copyright 2012 Google Inc. All Rights Reserved. - -# This script is only to be used by contributors. -# This will build everything, check out the latest tracing-framework gh-pages -# branch, update the bin/ path, and prepare a commit. - -# Break on error. -set -e - -# This must currently run from the root of the repo -# TODO(benvanik): make this runnable from anywhere (find git directory?) -if [ ! -d ".git" ]; then - echo "This script must be run from the root of the repository (the folder containing .git)" - exit 1 -fi - -GIT_USERNAME=`git config user.name` -GIT_USEREMAIL=`git config user.email` - -# ============================================================================= -# Build everything -# ============================================================================= -echo "Building everything..." - -./scripts/prepare-publish.sh -SRC_PATH=$PWD/build-bin/gh-pages/ - -echo "" -# ============================================================================= -# Look for sibling path or create -# ============================================================================= - -cd .. -if [ ! -d "tracing-framework-gh-pages" ]; then - # Not found - create and clone - echo "Creating tracing-framework-gh-pages..." - git clone git@github.com:google/tracing-framework.git tracing-framework-gh-pages - cd tracing-framework-gh-pages - git checkout gh-pages -else - # Reset hard to the current version - echo "Resetting tracing-framework-gh-pages..." - cd tracing-framework-gh-pages - git reset --hard - git pull - git merge origin/gh-pages -fi - -# Be sure to reset username/email to the owner of the source repo -git config user.name "$GIT_USERNAME" -git config user.email "$GIT_USEREMAIL" - -echo "" -# ============================================================================= -# Copy bin/ -# ============================================================================= -echo "Updating bin/..." - -# Delete all the old contents and recreate -if [ -d "bin" ]; then - rm -rf bin/ -fi -mkdir bin - -# Copy bin contents -cp -R $SRC_PATH/* bin/ - -echo "" -# ============================================================================= -# Stage all changes -# ============================================================================= -echo "Staging changes..." - -git add --all bin/ -git commit -m "Updating bin/ to the latest version." - -echo "" -# ============================================================================= -# Push! -# ============================================================================= -echo "Pushing changes..." - -git push origin gh-pages - -echo "" - diff --git a/scripts/update-version.sh b/scripts/update-version.sh index 7184ef21..b47c8e8e 100755 --- a/scripts/update-version.sh +++ b/scripts/update-version.sh @@ -52,13 +52,13 @@ sed -i.bak \ package.json rm package.json.bak -echo "-> extensions/wtf-injector-chrome/manifest.json" +echo "-> extensions/wtf-injector-chrome/manifest.template.json" # __"version": "2012.12.17.1", manifest_string="$ver_major.$ver_minor.$ver_patch.$ver_tag" sed -i.bak \ "s/^\ \ \"version\": \".*\",$/\ \ \"version\": \"$manifest_string\",/" \ - extensions/wtf-injector-chrome/manifest.json -rm extensions/wtf-injector-chrome/manifest.json.bak + extensions/wtf-injector-chrome/manifest.template.json +rm extensions/wtf-injector-chrome/manifest.template.json.bak echo "-> extensions/wtf-injector-firefox/package.json" # __"version": "2012.12.17.1", @@ -93,6 +93,6 @@ echo "Committing changes..." git commit -o -m "Updating version to $ver_string." \ package.json \ - extensions/wtf-injector-chrome/manifest.json \ + extensions/wtf-injector-chrome/manifest.template.json \ extensions/wtf-injector-firefox/package.json \ src/wtf/version.js diff --git a/src/BUILD b/src/BUILD new file mode 100644 index 00000000..c1024820 --- /dev/null +++ b/src/BUILD @@ -0,0 +1,9 @@ +# Description: +# WTF source code for both the tracing library and visualizers. + +package(default_visibility = ["//:internal"]) + +filegroup( + name = "all_files", + srcs = glob(["**/*.js"]), +) diff --git a/src/wtf/BUILD b/src/wtf/BUILD new file mode 100644 index 00000000..8d737f29 --- /dev/null +++ b/src/wtf/BUILD @@ -0,0 +1,19 @@ +# Description: +# Shared macros and versioning rules. + +package(default_visibility = ["//:internal"]) + +load("//builddefs:config.bzl", "COMMON_JS_SUPPRESSIONS") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library") + +closure_js_library( + name = "wtf", + srcs = [ + "version.js", + "wtf.js", + ], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + "//externs", + ], +) diff --git a/src/wtf/addon/BUILD b/src/wtf/addon/BUILD new file mode 100644 index 00000000..5593c5c0 --- /dev/null +++ b/src/wtf/addon/BUILD @@ -0,0 +1,23 @@ +# Description: +# Add-on API. + +package(default_visibility = ["//:internal"]) + +load("//builddefs:config.bzl", "COMMON_JS_SUPPRESSIONS") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library") + +closure_js_library( + name = "addon", + srcs = [ + "addon.js", + "appaddon.js", + "manifest.js", + "registry.js", + "traceaddon.js", + ], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + "//src/wtf/events:eventemitter", + "@io_bazel_rules_closure//closure/library", + ], +) diff --git a/src/wtf/app/BUILD b/src/wtf/app/BUILD new file mode 100644 index 00000000..27183f65 --- /dev/null +++ b/src/wtf/app/BUILD @@ -0,0 +1,135 @@ +# Description: +# Main application source code. +# HTML and assets live under //app/. + +package(default_visibility = ["//:internal"]) + +load("//builddefs:config.bzl", "COMMON_JS_SUPPRESSIONS") +load("//builddefs:less_rules.bzl", "less_css_library") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_template_library") + +closure_js_library( + name = "app", + srcs = [ + "addonmanager.js", + "app.js", + "documentview.js", + "exports.js", + "framepainter.js", + "granularity.js", + "graphics/canvaspanel.js", + "healthdialog.js", + "helpdialog.js", + "loader.js", + "maindisplay.js", + "markpainter.js", + "nav/heatmappainter.js", + "nav/navbar.js", + "nav/statsbox.js", + "nav/timelinepainter.js", + "query/querypanel.js", + "query/querytablesource.js", + "selection.js", + "selectionpainter.js", + "splashdialog.js", + "statusbar.js", + "tabbar.js", + "tabpanel.js", + "toolbar.js", + "tracks/statisticstablesource.js", + "tracks/timerangepainter.js", + "tracks/trackinfobar.js", + "tracks/trackspanel.js", + "tracks/zonepainter.js", + ], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + ":app_templates", + "//src/wtf", + "//src/wtf/addon", + "//src/wtf/data", + "//src/wtf/db", + "//src/wtf/db:unit", + "//src/wtf/doc", + "//src/wtf/events", + "//src/wtf/events:eventemitter", + "//src/wtf/io", + "//src/wtf/io/cff", + "//src/wtf/ipc", + "//src/wtf/math", + "//src/wtf/pal", + "//src/wtf/replay/graphics", + "//src/wtf/replay/graphics/ui", + "//src/wtf/replay/timetravel", + "//src/wtf/timing", + "//src/wtf/ui:color", + "//src/wtf/ui:control", + "//src/wtf/ui:dialog", + "//src/wtf/ui:errordialog", + "//src/wtf/ui:gridpainter", + "//src/wtf/ui:progressdialog", + "//src/wtf/ui:rangepainter", + "//src/wtf/ui:rulerpainter", + "//src/wtf/ui:searchcontrol", + "//src/wtf/ui:settingsdialog", + "//src/wtf/ui:timepainter", + "//src/wtf/ui:tooltip", + "//src/wtf/ui:virtualtable", + "//src/wtf/ui:zoom", + "//src/wtf/util", + "@io_bazel_rules_closure//closure/library", + ], +) + +closure_js_template_library( + name = "app_templates", + srcs = [ + "documentview.soy", + "graphics/canvaspanel.soy", + "healthdialog.soy", + "helpdialog.soy", + "maindisplay.soy", + "nav/navbar.soy", + "nav/statsbox.soy", + "query/querypanel.soy", + "splashdialog.soy", + "statusbar.soy", + "tabbar.soy", + "toolbar.soy", + "tracks/trackinfobar.soy", + "tracks/trackspanel.soy", + ], +) + +less_css_library( + name = "app_styles", + srcs = [ + "documentview.less", + "graphics/canvaspanel.less", + "graphics/graphics.less", + "healthdialog.less", + "helpdialog.less", + "maindisplay.less", + "nav/nav.less", + "nav/navbar.less", + "nav/statsbox.less", + "query/query.less", + "query/querypanel.less", + "splashdialog.less", + "statusbar.less", + "tabbar.less", + "toolbar.less", + "tracks/trackinfobar.less", + "tracks/tracks.less", + "tracks/trackspanel.less", + ], + includes = ["src"], + main_srcs = ["app.less"], + deps = [ + "//src/wtf/replay/graphics/ui:graphics_styles", + "//src/wtf/replay/timetravel:timetravel_styles", + "//src/wtf/ui:ui_styles", + "//src/wtf/ui/styles", + ], +) diff --git a/src/wtf/app/addonmanager.js b/src/wtf/app/addonmanager.js index c94d451b..f03685aa 100644 --- a/src/wtf/app/addonmanager.js +++ b/src/wtf/app/addonmanager.js @@ -18,7 +18,6 @@ goog.require('goog.Disposable'); goog.require('goog.Uri'); goog.require('goog.asserts'); goog.require('goog.dom'); -goog.require('goog.dom.TagName'); goog.require('goog.dom.classes'); goog.require('goog.style'); goog.require('wtf.addon'); @@ -61,7 +60,7 @@ wtf.app.AddonManager = function(documentView) { * @type {!Element} * @private */ - this.addonsEl_ = dom.createElement(goog.dom.TagName.DIV); + this.addonsEl_ = dom.createElement('div'); this.addonsEl_.id = 'wtfAppAddons'; dom.getDocument().body.appendChild(this.addonsEl_); @@ -124,13 +123,13 @@ wtf.app.AddonManager.prototype.loadAddon = function(addon) { // Create iframe container. var iframe = /** @type {!HTMLIFrameElement} */ ( - dom.createElement(goog.dom.TagName.IFRAME)); + dom.createElement('iframe')); this.addonsEl_.appendChild(iframe); // Set base. var idoc = iframe.contentDocument; var baseUri = goog.Uri.resolve(window.location.href, manifest.getUrl()); - var baseEl = idoc.createElement(goog.dom.TagName.BASE); + var baseEl = idoc.createElement('base'); baseEl.href = baseUri.toString(); idoc.head.appendChild(baseEl); @@ -141,7 +140,7 @@ wtf.app.AddonManager.prototype.loadAddon = function(addon) { // Add scripts. for (var n = 0; n < info.scripts.length; n++) { - var script = idoc.createElement(goog.dom.TagName.SCRIPT); + var script = idoc.createElement('script'); script.src = info.scripts[n]; idoc.body.appendChild(script); } @@ -183,7 +182,7 @@ wtf.app.AddonManager.prototype.setupAddonApi_ = function( function createTabPanel(path, name, options, callback) { tabbar.addPanel(new wtf.app.AddonTabPanel( addon, documentView, path, name, options, callback)); - }; + } }; @@ -267,7 +266,7 @@ wtf.app.AddonTabPanel.Callback; * @override */ wtf.app.AddonTabPanel.prototype.createDom = function(dom) { - var el = dom.createElement(goog.dom.TagName.DIV); + var el = dom.createElement('div'); goog.dom.classes.add(el, goog.getCssName('appUiTabPanel')); return el; }; @@ -287,13 +286,13 @@ wtf.app.AddonTabPanel.prototype.setupIframe_ = function() { // renamed CSS. Better would be to insert a default stylesheet with some // common styles (buttons/etc). var iframe = /** @type {!HTMLIFrameElement} */ ( - this.getDom().createElement(goog.dom.TagName.IFRAME)); + this.getDom().createElement('iframe')); this.getRootElement().appendChild(iframe); // Set base. var idoc = iframe.contentDocument; var baseUri = goog.Uri.resolve(window.location.href, manifest.getUrl()); - var baseEl = idoc.createElement(goog.dom.TagName.BASE); + var baseEl = idoc.createElement('base'); baseEl.href = baseUri.toString(); idoc.head.appendChild(baseEl); @@ -306,7 +305,7 @@ wtf.app.AddonTabPanel.prototype.setupIframe_ = function() { // Add scripts. var scripts = this.options_['scripts'] || []; for (var n = 0; n < scripts.length; n++) { - var script = idoc.createElement(goog.dom.TagName.SCRIPT); + var script = idoc.createElement('script'); script.src = scripts[n]; idoc.body.appendChild(script); } @@ -314,7 +313,7 @@ wtf.app.AddonTabPanel.prototype.setupIframe_ = function() { // Add stylesheets. var stylesheets = this.options_['stylesheets'] || []; for (var n = 0; n < stylesheets.length; n++) { - var link = idoc.createElement(goog.dom.TagName.LINK); + var link = idoc.createElement('link'); link.rel = 'stylesheet'; link.href = stylesheets[n]; link.type = 'text/css'; diff --git a/src/wtf/app/documentview.js b/src/wtf/app/documentview.js index 9e77d18b..51fef9a9 100644 --- a/src/wtf/app/documentview.js +++ b/src/wtf/app/documentview.js @@ -581,7 +581,7 @@ wtf.app.DocumentView.prototype.saveLocalTrace_ = function() { self.getDom()); }; xhr.send(); - }; + } }; diff --git a/src/wtf/app/framepainter.js b/src/wtf/app/framepainter.js index 01fcb346..4e56c827 100644 --- a/src/wtf/app/framepainter.js +++ b/src/wtf/app/framepainter.js @@ -237,7 +237,7 @@ wtf.app.FramePainter.prototype.getInfoStringInternal = * @param {number} x X coordinate, relative to canvas. * @param {number} y Y coordinate, relative to canvas. * @param {!goog.math.Rect} bounds Draw bounds. - * @return {wtf.db.Frame|Array.} Frame or an array + * @return {!wtf.db.Frame|!Array.} Frame or an array * containing the two frames on either side of the time. * @private */ diff --git a/src/wtf/app/granularity.js b/src/wtf/app/granularity.js index 7304bb18..351013a4 100644 --- a/src/wtf/app/granularity.js +++ b/src/wtf/app/granularity.js @@ -27,10 +27,11 @@ wtf.app.Granularity = { CENTISECOND: 10, /** ms */ MILLISECOND: 1, - - // TODO(benvanik): make this a setting on the summary index instead? - /** - * The finest granularity to work with. - */ - FINEST: 100 }; + +// TODO(benvanik): make this a setting on the summary index instead? +/** + * The finest granularity to work with. + * @const {wtf.app.Granularity} + */ +wtf.app.Granularity.FINEST = wtf.app.Granularity.DECISECOND; diff --git a/src/wtf/app/helpdialog.soy b/src/wtf/app/helpdialog.soy index 2257f0ef..408aa899 100644 --- a/src/wtf/app/helpdialog.soy +++ b/src/wtf/app/helpdialog.soy @@ -21,7 +21,7 @@ * @param system_key System command key string (like 'ctrl'). * @param is_chrome_extension Whether this is running in the Chrome extension. */ -{template .control} +{template .control autoescape="deprecated-contextual"}

diff --git a/src/wtf/app/loader.js b/src/wtf/app/loader.js index 53e1cdbe..f16103cd 100644 --- a/src/wtf/app/loader.js +++ b/src/wtf/app/loader.js @@ -189,6 +189,7 @@ wtf.app.Loader.prototype.requestLocalOpenDialog = function( opt_selectCallback.call(opt_scope); } _gaq.push(['_trackEvent', 'app', 'open_local_files']); + goog.asserts.assert(inputElement.files); this.loadFiles(inputElement.files); }, false, this); inputElement.click(); @@ -269,7 +270,7 @@ wtf.app.Loader.prototype.requestDriveOpenDialog = function( } this.loadDataSources_(sourceInfos); - }; + } }, this); }; @@ -277,7 +278,7 @@ wtf.app.Loader.prototype.requestDriveOpenDialog = function( /** * Begins loading a set of HTML5 File objects. * These can be from the filesystem, dragged in, etc. - * @param {!Array.} files File objects. + * @param {!Array.|!FileList} files File objects. */ wtf.app.Loader.prototype.loadFiles = function(files) { var sourceInfos = []; @@ -439,7 +440,7 @@ wtf.app.Loader.prototype.loadSucceeded_ = function(doc, entries, opt_title) { wtf.timing.setTimeout(50, function() { documentView.zoomToFit(); }, this); - }; + } }; diff --git a/src/wtf/app/maindisplay.js b/src/wtf/app/maindisplay.js index 714489b9..5123204e 100644 --- a/src/wtf/app/maindisplay.js +++ b/src/wtf/app/maindisplay.js @@ -193,13 +193,13 @@ wtf.app.MainDisplay.prototype.createDom = function(dom) { wtf.app.MainDisplay.prototype.setupDragDropLoading_ = function() { var doc = this.getDom().getDocument(); var eh = this.getHandler(); - eh.listen(doc.body, goog.events.EventType.DRAGENTER, function(e) { + eh.listenWithScope(doc.body, goog.events.EventType.DRAGENTER, function(e) { e.preventDefault(); }, false, this); - eh.listen(doc.body, goog.events.EventType.DRAGOVER, function(e) { + eh.listenWithScope(doc.body, goog.events.EventType.DRAGOVER, function(e) { e.preventDefault(); }, false, this); - eh.listen(doc.body, goog.events.EventType.DROP, function(e) { + eh.listenWithScope(doc.body, goog.events.EventType.DROP, function(e) { var browserEvent = e.getBrowserEvent(); if (browserEvent.dataTransfer && browserEvent.dataTransfer.files && browserEvent.dataTransfer.files.length) { diff --git a/src/wtf/app/query/querypanel.js b/src/wtf/app/query/querypanel.js index 94f1ed6d..c244bc1e 100644 --- a/src/wtf/app/query/querypanel.js +++ b/src/wtf/app/query/querypanel.js @@ -72,7 +72,8 @@ wtf.app.query.QueryPanel = function(documentView) { */ this.zoneSelect_ = this.getChildElement(goog.getCssName('zoneSelect')); this.updateZoneList_(); - this.getHandler().listen(this.zoneSelect_, goog.events.EventType.CHANGE, + this.getHandler().listenWithScope( + this.zoneSelect_, goog.events.EventType.CHANGE, this.reissueQuery_, false, this); /** diff --git a/src/wtf/app/splashdialog.soy b/src/wtf/app/splashdialog.soy index e22f8865..a70f4724 100644 --- a/src/wtf/app/splashdialog.soy +++ b/src/wtf/app/splashdialog.soy @@ -21,7 +21,7 @@ * @param system_key System command key string (like 'ctrl'). * @param show_drive Whether to show the open from drive button. */ -{template .control} +{template .control autoescape="deprecated-contextual"}

Web Tracing Framework

diff --git a/src/wtf/app/statusbar.soy b/src/wtf/app/statusbar.soy index 9b1bcfd2..d23654e9 100644 --- a/src/wtf/app/statusbar.soy +++ b/src/wtf/app/statusbar.soy @@ -18,7 +18,7 @@ * Root control UI. * @param system_key System command key string (like 'ctrl'). */ -{template .control} +{template .control autoescape="deprecated-contextual"}
} + * @type {!Object.} * @private */ this.panelsByPath_ = {}; @@ -114,7 +114,7 @@ wtf.app.Tabbar.prototype.addPanel = function(panel) { dom.appendChild( this.getChildElement(goog.getCssName('kTabHeaderTabs')), tabElement); - this.getHandler().listen( + this.getHandler().listenWithScope( tabElement, goog.events.EventType.CLICK, function(e) { @@ -281,8 +281,7 @@ wtf.app.Tabbar.prototype.getTabForPanel_ = function(panel) { /** - * Handles sizing/layout. - * This is called by the document view when the control size changes. + * @override */ wtf.app.Tabbar.prototype.layout = function() { if (this.selectedTab_) { diff --git a/src/wtf/app/toolbar.soy b/src/wtf/app/toolbar.soy index 54841290..208795ba 100644 --- a/src/wtf/app/toolbar.soy +++ b/src/wtf/app/toolbar.soy @@ -18,7 +18,7 @@ * Root control UI. * @param system_key System command key string (like 'ctrl'). */ -{template .control} +{template .control autoescape="deprecated-contextual"}
diff --git a/src/wtf/app/tracks/trackspanel.js b/src/wtf/app/tracks/trackspanel.js index e6494e93..2d8e7bea 100644 --- a/src/wtf/app/tracks/trackspanel.js +++ b/src/wtf/app/tracks/trackspanel.js @@ -293,7 +293,7 @@ wtf.app.tracks.TracksPanel.prototype.setupKeyboardShortcuts_ = function() { commandManager.execute('goto_frame', this, null, newFrame); } } - }; + } keyboardScope.addShortcut('z', function() { moveFrames(-1, true); }, this); diff --git a/src/wtf/data/BUILD b/src/wtf/data/BUILD new file mode 100644 index 00000000..d3b01cb5 --- /dev/null +++ b/src/wtf/data/BUILD @@ -0,0 +1,25 @@ +# Description: +# Event data types. + +package(default_visibility = ["//:internal"]) + +load("//builddefs:config.bzl", "COMMON_JS_SUPPRESSIONS") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library") + +closure_js_library( + name = "data", + srcs = [ + "contextinfo.js", + "eventclass.js", + "eventflag.js", + "formats.js", + "variable.js", + "webidl.js", + "zonetype.js", + ], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + "//src/wtf", + "@io_bazel_rules_closure//closure/library", + ], +) diff --git a/src/wtf/data/contextinfo.js b/src/wtf/data/contextinfo.js index bf675e8f..7a3ca206 100644 --- a/src/wtf/data/contextinfo.js +++ b/src/wtf/data/contextinfo.js @@ -77,8 +77,7 @@ wtf.data.ContextInfo.prototype.serialize = goog.abstractMethod; /** - * Gets a human-readable version of the context info. - * @return {string} String version. + * @override */ wtf.data.ContextInfo.prototype.toString = goog.abstractMethod; diff --git a/src/wtf/db/BUILD b/src/wtf/db/BUILD new file mode 100644 index 00000000..2ca6c9d1 --- /dev/null +++ b/src/wtf/db/BUILD @@ -0,0 +1,73 @@ +# Description: +# Trace database loading and querying. + +package(default_visibility = ["//:internal"]) + +load("//builddefs:config.bzl", "COMMON_JS_SUPPRESSIONS") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library") + +closure_js_library( + name = "db", + srcs = [ + "database.js", + "datasource.js", + "datasourceinfo.js", + "db.js", + "eventindex.js", + "eventiterator.js", + "eventlist.js", + "eventstatistics.js", + "eventstruct.js", + "eventtype.js", + "eventtypebuilder.js", + "eventtypetable.js", + "exports.js", + "filter.js", + "filterparser.js", + "frame.js", + "framelist.js", + "healthinfo.js", + "mark.js", + "marklist.js", + "queryresult.js", + "sources/callsdatasource.js", + "sources/chunkeddatasource.js", + "sources/cpuprofiledatasource.js", + "timerange.js", + "timerangelist.js", + "zone.js", + ], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + ":unit", + "//src/wtf", + "//src/wtf/data", + "//src/wtf/events:eventemitter", + "//src/wtf/io", + "//src/wtf/io/cff", + "//src/wtf/pal", + "//src/wtf/util", + "@io_bazel_rules_closure//closure/library", + ], +) + +closure_js_library( + name = "node", + srcs = ["node.js"], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + ":db", + "//src/wtf/pal", + "@io_bazel_rules_closure//closure/library", + ], +) + +closure_js_library( + name = "unit", + srcs = ["unit.js"], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + "//src/wtf/util", + "@io_bazel_rules_closure//closure/library", + ], +) diff --git a/src/wtf/db/eventiterator.js b/src/wtf/db/eventiterator.js index 735b7f74..3d5fbdd4 100644 --- a/src/wtf/db/eventiterator.js +++ b/src/wtf/db/eventiterator.js @@ -607,7 +607,7 @@ wtf.db.EventIterator.prototype.buildArgumentString_ = function( // TODO(benvanik): better handling of objects/other types? s.push(value); } - }; + } }; diff --git a/src/wtf/db/eventtype.js b/src/wtf/db/eventtype.js index 02dd60a5..1724028f 100644 --- a/src/wtf/db/eventtype.js +++ b/src/wtf/db/eventtype.js @@ -106,8 +106,7 @@ wtf.db.EventType.LegacyParseFunction; /** - * Gets a pretty-formatted name for the event. - * @return {string} Pretty-formatted name. + * @override */ wtf.db.EventType.prototype.toString = function() { return this.name; diff --git a/src/wtf/db/filter.js b/src/wtf/db/filter.js index 15ea3750..81a6c0ba 100644 --- a/src/wtf/db/filter.js +++ b/src/wtf/db/filter.js @@ -203,8 +203,7 @@ wtf.db.Filter.prototype.setFromString = function(value) { /** - * Gets a string representing this filter. - * @return {string} Filter string. May be the empty string. + * @override */ wtf.db.Filter.prototype.toString = function() { return this.sourceString_; @@ -368,9 +367,9 @@ wtf.db.Filter.prototype.generateArgumentFilter_ = function(expr) { } else { return findArgumentName(access.base); } - }; + } return findArgumentName(exprValue.value); - }; + } function stringifyExpressionValue(exprValue) { switch (exprValue.type) { case 'number': @@ -397,7 +396,7 @@ wtf.db.Filter.prototype.generateArgumentFilter_ = function(expr) { default: throw new Error('Unknown expression value type: ' + exprValue.type); } - }; + } function stringifyReferenceAccess(access) { if (goog.isString(access)) { if (access[0] == '@') { @@ -423,7 +422,7 @@ wtf.db.Filter.prototype.generateArgumentFilter_ = function(expr) { '"' + access.name + '"' : access.name; return stringifyReferenceAccess(access.base) + '[' + name + ']'; } - }; + } }; diff --git a/src/wtf/db/healthinfo.js b/src/wtf/db/healthinfo.js index dd33b8f4..d2e1dc53 100644 --- a/src/wtf/db/healthinfo.js +++ b/src/wtf/db/healthinfo.js @@ -416,8 +416,7 @@ wtf.db.HealthWarning.prototype.getLink = function() { /** - * Gets a human-readable string for this warning. - * @return {string} Warning string. + * @override */ wtf.db.HealthWarning.prototype.toString = function() { return this.title_ + ' (' + this.suggestion_ + ')'; diff --git a/src/wtf/db/queryresult.js b/src/wtf/db/queryresult.js index 3667af7a..1f943046 100644 --- a/src/wtf/db/queryresult.js +++ b/src/wtf/db/queryresult.js @@ -151,7 +151,7 @@ wtf.db.QueryResult.prototype.dump = function(format) { /** * Dumps the results into a blob formatted by RFC 4180 (CSV). - * @return {string?} Results. + * @return {string} Results. * @private */ wtf.db.QueryResult.prototype.dumpCsv_ = function() { diff --git a/src/wtf/db/sources/callsdatasource.js b/src/wtf/db/sources/callsdatasource.js index 2c9dc3b2..aa1afae0 100644 --- a/src/wtf/db/sources/callsdatasource.js +++ b/src/wtf/db/sources/callsdatasource.js @@ -64,7 +64,7 @@ wtf.db.sources.CallsDataSource = function(db, sourceInfo, transport) { /** * All received blob parts. * They'll be stitched up at the end. - * @type {!Array.} + * @type {!Array.} * @private */ this.blobParts_ = []; diff --git a/src/wtf/db/sources/cpuprofiledatasource.js b/src/wtf/db/sources/cpuprofiledatasource.js index 8ff69cce..5b56db62 100644 --- a/src/wtf/db/sources/cpuprofiledatasource.js +++ b/src/wtf/db/sources/cpuprofiledatasource.js @@ -60,7 +60,7 @@ wtf.db.sources.CpuProfileDataSource = function(db, sourceInfo, transport) { /** * All received blob parts. * They'll be stitched up at the end. - * @type {!Array.} + * @type {!Array.} * @private */ this.blobParts_ = []; diff --git a/src/wtf/db/zone.js b/src/wtf/db/zone.js index 31825760..cd4c60f9 100644 --- a/src/wtf/db/zone.js +++ b/src/wtf/db/zone.js @@ -139,8 +139,7 @@ wtf.db.Zone.prototype.resetInfo = function(name, type, location) { /** - * Gets a string representation of the zone. - * @return {string} String representation of the zone. + * @override */ wtf.db.Zone.prototype.toString = function() { return this.name_; @@ -265,7 +264,7 @@ wtf.db.Zone.prototype.getSharedIndex = function(eventNames) { * Queries the zone. * Throws errors if the expression could not be parsed. * @param {string} expr Query string. - * @return {wtf.db.QueryResult} Result. + * @return {!wtf.db.QueryResult} Result. */ wtf.db.Zone.prototype.query = function(expr) { var startTime = wtf.now(); diff --git a/src/wtf/doc/BUILD b/src/wtf/doc/BUILD new file mode 100644 index 00000000..c90610cb --- /dev/null +++ b/src/wtf/doc/BUILD @@ -0,0 +1,26 @@ +# Description: +# Abstract loaded trace document. + +package(default_visibility = ["//:internal"]) + +load("//builddefs:config.bzl", "COMMON_JS_SUPPRESSIONS") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library") + +closure_js_library( + name = "doc", + srcs = [ + "comment.js", + "commentscope.js", + "document.js", + "profile.js", + "profilescope.js", + "view.js", + ], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + "//src/wtf", + "//src/wtf/db", + "//src/wtf/events:eventemitter", + "@io_bazel_rules_closure//closure/library", + ], +) diff --git a/src/wtf/events/BUILD b/src/wtf/events/BUILD new file mode 100644 index 00000000..ba74f604 --- /dev/null +++ b/src/wtf/events/BUILD @@ -0,0 +1,37 @@ +# Description: +# Eventing and common app event types. + +package(default_visibility = ["//:internal"]) + +load("//builddefs:config.bzl", "COMMON_JS_SUPPRESSIONS") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library") + +closure_js_library( + name = "events", + srcs = [ + "commandmanager.js", + "events.js", + "keyboard.js", + ], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + ":eventemitter", + "//src/wtf/util", + "@io_bazel_rules_closure//closure/library", + ], +) + +closure_js_library( + name = "eventemitter", + srcs = [ + "eventemitter.js", + "eventfullist.js", + "eventfulmap.js", + "eventtype.js", + ], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + "//src/wtf", + "@io_bazel_rules_closure//closure/library", + ], +) diff --git a/src/wtf/events/keyboard.js b/src/wtf/events/keyboard.js index 8130e1e8..c3994879 100644 --- a/src/wtf/events/keyboard.js +++ b/src/wtf/events/keyboard.js @@ -315,7 +315,7 @@ wtf.events.KeyboardScope.prototype.addCommandShortcut = function( if (commandManager) { commandManager.execute(commandName, this, null); } - }; + } this.listeners_.push([shortcut, callback, this]); if (this.enabled_) { this.keyboard_.addShortcut_(shortcut, callback, this); diff --git a/src/wtf/hud/BUILD b/src/wtf/hud/BUILD new file mode 100644 index 00000000..3aa8db5c --- /dev/null +++ b/src/wtf/hud/BUILD @@ -0,0 +1,54 @@ +# Description: +# Browser tracing HUD. + +package(default_visibility = ["//:internal"]) + +load("//builddefs:config.bzl", "COMMON_JS_SUPPRESSIONS") +load("//builddefs:less_rules.bzl", "less_css_library") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_template_library") + +closure_js_library( + name = "hud", + srcs = [ + "exports.js", + "hud.js", + "livegraph.js", + "overlay.js", + ], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + ":overlay_template", + "//src/wtf/addon", + "//src/wtf/events", + "//src/wtf/io", + "//src/wtf/ipc", + "//src/wtf/trace", + "//src/wtf/trace:util", + "//src/wtf/ui:control", + "//src/wtf/ui:errordialog", + "//src/wtf/ui:icons", + "//src/wtf/ui:settingsdialog", + "//src/wtf/util", + "@io_bazel_rules_closure//closure/library", + ], +) + +less_css_library( + name = "hud_styles", + srcs = [ + "livegraph.less", + "overlay.less", + ], + includes = ["src"], + main_srcs = ["hud.less"], + deps = [ + "//src/wtf/ui:ui_styles", + "//src/wtf/ui/styles", + ], +) + +closure_js_template_library( + name = "overlay_template", + srcs = ["overlay.soy"], +) diff --git a/src/wtf/hud/hud.js b/src/wtf/hud/hud.js index 43e0cace..a80d446f 100644 --- a/src/wtf/hud/hud.js +++ b/src/wtf/hud/hud.js @@ -57,7 +57,8 @@ wtf.hud.prepare = function(opt_options, opt_parentElement) { var provider = providers[n]; var buttons = provider.getHudButtons(); for (var m = 0; m < buttons.length; m++) { - wtf.hud.buttons_.push(buttons[m]); + wtf.hud.buttons_.push( + /** @type {!wtf.hud.Overlay.ButtonInfo} */ (buttons[m])); } } diff --git a/src/wtf/hud/overlay.less b/src/wtf/hud/overlay.less index 2c3a4484..3b09684e 100644 --- a/src/wtf/hud/overlay.less +++ b/src/wtf/hud/overlay.less @@ -9,6 +9,8 @@ * @author benvanik@google.com (Ben Vanik) */ + @import "wtf/ui/styles/css3.less"; + .hudOuter { position: fixed; height: 40px; @@ -52,12 +54,7 @@ border-left: 1px solid #C1C1C1; text-align: center; color: #444; - background-color: #f5f5f5; - background-image: -webkit-linear-gradient(top,#f5f5f5,#f1f1f1); - background-image: -moz-linear-gradient(top,#f5f5f5,#f1f1f1); - background-image: -ms-linear-gradient(top,#f5f5f5,#f1f1f1); - background-image: -o-linear-gradient(top,#f5f5f5,#f1f1f1); - background-image: linear-gradient(top,#f5f5f5,#f1f1f1); + .backgroundGradient(#f5f5f5, #f1f1f1); -webkit-transition: all 0.218s; -moz-transition: all 0.218s; @@ -66,25 +63,13 @@ } .hudButton:hover { color: #222; - background-color: #f8f8f8; - background-image: -webkit-linear-gradient(top,#f8f8f8,#f1f1f1); - background-image: -moz-linear-gradient(top,#f8f8f8,#f1f1f1); - background-image: -ms-linear-gradient(top,#f8f8f8,#f1f1f1); - background-image: -o-linear-gradient(top,#f8f8f8,#f1f1f1); - background-image: linear-gradient(top,#f8f8f8,#f1f1f1); - -webkit-box-shadow: inset 0px 1px 2px rgba(0,0,0,0.2); - box-shadow: inset 0px 1px 2px rgba(0,0,0,0.2); + .backgroundGradient(#f8f8f8 ,#f1f1f1); + .insetBoxShadow(0px, 1px, 2px, rgba(0,0,0,0.2)); } .hudButton:active { color: #333; - background-color: #f6f6f6; - background-image: -webkit-linear-gradient(top,#f6f6f6,#f1f1f1); - background-image: -moz-linear-gradient(top,#f6f6f6,#f1f1f1); - background-image: -ms-linear-gradient(top,#f6f6f6,#f1f1f1); - background-image: -o-linear-gradient(top,#f6f6f6,#f1f1f1); - background-image: linear-gradient(top,#f6f6f6,#f1f1f1); - -webkit-box-shadow: inset 0px 1px 3px rgba(0,0,0,0.4); - box-shadow: inset 0px 1px 3px rgba(0,0,0,0.4); + .backgroundGradient(#f6f6f6, #f1f1f1); + .insetBoxShadow(0px, 1px, 3px, rgba(0,0,0,0.4)); } .hudButton img { position: relative; diff --git a/src/wtf/io/BUILD b/src/wtf/io/BUILD new file mode 100644 index 00000000..52e96839 --- /dev/null +++ b/src/wtf/io/BUILD @@ -0,0 +1,41 @@ +# Description: +# IO subsystem for basic IO operations and the WTF file formats. + +package(default_visibility = ["//:internal"]) + +load("//builddefs:config.bzl", "COMMON_JS_SUPPRESSIONS") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library") + +closure_js_library( + name = "io", + srcs = [ + "blob.js", + "buffer.js", + "bufferview.js", + "drive.js", + "eventtype.js", + "io.js", + "readtransport.js", + # TODO(benvanik): figure out if I lost this code. + #"streaminghttpwritestream.js", + "stringtable.js", + "writetransport.js", + "transports/blobreadtransport.js", + "transports/blobwritetransport.js", + "transports/filereadtransport.js", + "transports/filewritetransport.js", + "transports/memoryreadtransport.js", + "transports/memorywritetransport.js", + "transports/nullwritetransport.js", + "transports/xhrreadtransport.js", + "transports/xhrwritetransport.js", + ], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + "//src/wtf", + "//src/wtf/events:eventemitter", + "//src/wtf/pal", + "//src/wtf/timing", + "@io_bazel_rules_closure//closure/library", + ], +) diff --git a/src/wtf/io/cff/BUILD b/src/wtf/io/cff/BUILD new file mode 100644 index 00000000..54547d6f --- /dev/null +++ b/src/wtf/io/cff/BUILD @@ -0,0 +1,40 @@ +# Description: +# WTF chunked file format reading/writing. + +package(default_visibility = ["//:internal"]) + +load("//builddefs:config.bzl", "COMMON_JS_SUPPRESSIONS") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library") + +closure_js_library( + name = "cff", + srcs = [ + "binarystreamsource.js", + "binarystreamtarget.js", + "chunk.js", + "chunks/eventdatachunk.js", + "chunks/fileheaderchunk.js", + "chunktype.js", + "jsonstreamsource.js", + "jsonstreamtarget.js", + "part.js", + "parts/binaryeventbufferpart.js", + "parts/fileheaderpart.js", + "parts/jsoneventbufferpart.js", + "parts/legacyeventbufferpart.js", + "parts/resourcepart.js", + "parts/stringtablepart.js", + "parttype.js", + "streambase.js", + "streamsource.js", + "streamtarget.js", + ], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + "//src/wtf", + "//src/wtf/data", + "//src/wtf/events:eventemitter", + "//src/wtf/io", + "@io_bazel_rules_closure//closure/library", + ], +) diff --git a/src/wtf/io/drive.js b/src/wtf/io/drive.js index 34aa948a..f7ec24e6 100644 --- a/src/wtf/io/drive.js +++ b/src/wtf/io/drive.js @@ -215,7 +215,7 @@ wtf.io.drive.showFilePicker = function(options) { }); } goog.result.wait(wtf.io.drive.filePickerLoadResult_, show); - }; + } function show() { var mimeTypes = [ @@ -253,7 +253,7 @@ wtf.io.drive.showFilePicker = function(options) { } var builtPicker = picker.build(); builtPicker.setVisible(true); - }; + } return result; }; @@ -309,7 +309,7 @@ wtf.io.drive.queryFile = function(fileId) { }); } goog.result.wait(wtf.io.drive.downloadFileLoadResult_, getMetadata); - }; + } function getMetadata() { var token = gapi.auth.getToken(); @@ -343,7 +343,7 @@ wtf.io.drive.queryFile = function(fileId) { result.setError(); }; xhr.send(null); - }; + } return result; }; @@ -379,7 +379,7 @@ wtf.io.drive.downloadFile = function(driveFile) { }); } goog.result.wait(wtf.io.drive.downloadFileLoadResult_, download); - }; + } function download() { var token = gapi.auth.getToken(); @@ -402,7 +402,7 @@ wtf.io.drive.downloadFile = function(driveFile) { // We don't send here, as we let the caller do it. // xhr.send(null); result.setValue(xhr); - }; + } return result; }; diff --git a/src/wtf/io/transports/blobwritetransport.js b/src/wtf/io/transports/blobwritetransport.js index ea7bfd4f..ba533874 100644 --- a/src/wtf/io/transports/blobwritetransport.js +++ b/src/wtf/io/transports/blobwritetransport.js @@ -71,7 +71,7 @@ wtf.io.transports.BlobWriteTransport.prototype.disposeInternal = function() { wtf.io.transports.BlobWriteTransport.prototype.write = function(data) { if (wtf.io.Blob.isBlob(data)) { // Blobs are immutable, so store off our input. - this.blobParts_.push(data); + this.blobParts_.push(/** @type {!wtf.io.Blob} */ (data)); } else { // Wrap in a blob. This allows the browser to page the data out if needed. this.blobParts_.push(wtf.io.Blob.create([data])); diff --git a/src/wtf/ipc/BUILD b/src/wtf/ipc/BUILD new file mode 100644 index 00000000..bf31e82d --- /dev/null +++ b/src/wtf/ipc/BUILD @@ -0,0 +1,24 @@ +# Description: +# IPC utilities for communicating with remote code (like the extension). + +package(default_visibility = ["//:internal"]) + +load("//builddefs:config.bzl", "COMMON_JS_SUPPRESSIONS") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library") + +closure_js_library( + name = "ipc", + srcs = [ + "channel.js", + "ipc.js", + "messagechannel.js", + ], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + "//src/wtf/events:eventemitter", + "//src/wtf/timing", + "//src/wtf/trace:util", + "//src/wtf/util", + "@io_bazel_rules_closure//closure/library", + ], +) diff --git a/src/wtf/math/BUILD b/src/wtf/math/BUILD new file mode 100644 index 00000000..c64ce571 --- /dev/null +++ b/src/wtf/math/BUILD @@ -0,0 +1,16 @@ +# Description: +# Math utilities. + +package(default_visibility = ["//:internal"]) + +load("//builddefs:config.bzl", "COMMON_JS_SUPPRESSIONS") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library") + +closure_js_library( + name = "math", + srcs = [ + "math.js", + "mersennetwister.js", + ], + suppress = COMMON_JS_SUPPRESSIONS, +) diff --git a/src/wtf/pal/BUILD b/src/wtf/pal/BUILD new file mode 100644 index 00000000..f66a2118 --- /dev/null +++ b/src/wtf/pal/BUILD @@ -0,0 +1,23 @@ +# Description: +# Platform abstraction layer for supporting browser and non-browser runtimes. + +package(default_visibility = ["//:internal"]) + +load("//builddefs:config.bzl", "COMMON_JS_SUPPRESSIONS") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library") + +closure_js_library( + name = "pal", + srcs = [ + "browserplatform.js", + "chromeplatform.js", + "iplatform.js", + "nodeplatform.js", + "pal.js", + ], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + "//src/wtf", + "@io_bazel_rules_closure//closure/library", + ], +) diff --git a/src/wtf/pal/browserplatform.js b/src/wtf/pal/browserplatform.js index 4ad8e71c..5ac201a9 100644 --- a/src/wtf/pal/browserplatform.js +++ b/src/wtf/pal/browserplatform.js @@ -14,7 +14,6 @@ goog.provide('wtf.pal.BrowserPlatform'); goog.require('goog.dom'); -goog.require('goog.dom.TagName'); goog.require('goog.events.EventType'); goog.require('goog.fs'); goog.require('wtf.pal.IPlatform'); @@ -95,7 +94,7 @@ wtf.pal.BrowserPlatform.prototype.downloadBlob_ = function(filename, blob) { // Download file. Wow. var doc = goog.dom.getDocument(); - var a = doc.createElement(goog.dom.TagName.A); + var a = doc.createElement('a'); a['download'] = filename; a.href = goog.fs.createObjectUrl(blob); var e = doc.createEvent('MouseEvents'); diff --git a/src/wtf/remote/BUILD b/src/wtf/remote/BUILD new file mode 100644 index 00000000..715e98f9 --- /dev/null +++ b/src/wtf/remote/BUILD @@ -0,0 +1,24 @@ +# Description: +# Remote tracing support. + +package(default_visibility = ["//:internal"]) + +load("//builddefs:config.bzl", "COMMON_JS_SUPPRESSIONS") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library") + +closure_js_library( + name = "remote", + srcs = [ + "client.js", + "exports.js", + "remote.js", + ], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + "//src/wtf/data", + "//src/wtf/events", + "//src/wtf/io", + "//src/wtf/trace", + "@io_bazel_rules_closure//closure/library", + ], +) diff --git a/src/wtf/replay/graphics/BUILD b/src/wtf/replay/graphics/BUILD new file mode 100644 index 00000000..b84cdef0 --- /dev/null +++ b/src/wtf/replay/graphics/BUILD @@ -0,0 +1,61 @@ +# Description: +# Graphics replay. + +package(default_visibility = ["//:internal"]) + +load("//builddefs:config.bzl", "COMMON_JS_SUPPRESSIONS") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library") + +closure_js_library( + name = "graphics", + srcs = [ + "exports.js", + "graphics.js", + "session.js", + ], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + ":graphics_library", + "//src/wtf", + "//src/wtf/db", + "//src/wtf/events", + "//src/wtf/events:eventemitter", + "//src/wtf/replay/graphics/ui", + "//src/wtf/timing", + "//src/wtf/util", + "@io_bazel_rules_closure//closure/library", + ], +) + +closure_js_library( + name = "graphics_library", + srcs = [ + "contextpool.js", + "drawcallvisualizer.js", + "experiment.js", + "extensionmanager.js", + "frameoverdrawvisualizer.js", + "frametimevisualizer.js", + "highlightvisualizer.js", + "offscreensurface.js", + "overdrawsurface.js", + "overdrawvisualizer.js", + "playback.js", + "program.js", + "replayframe.js", + "skipcallsvisualizer.js", + "step.js", + "visualizer.js", + "webglstate.js", + ], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + "//src/wtf", + "//src/wtf/db", + "//src/wtf/events", + "//src/wtf/events:eventemitter", + "//src/wtf/timing", + "//src/wtf/util", + "@io_bazel_rules_closure//closure/library", + ], +) diff --git a/src/wtf/replay/graphics/contextpool.js b/src/wtf/replay/graphics/contextpool.js index 2fee5c10..0f518f32 100644 --- a/src/wtf/replay/graphics/contextpool.js +++ b/src/wtf/replay/graphics/contextpool.js @@ -128,7 +128,8 @@ wtf.replay.graphics.ContextPool.prototype.getContext = } else { // Create a new context. var newCanvas = this.dom_.createElement(goog.dom.TagName.CANVAS); - retrievedContext = newCanvas.getContext(contextType, opt_attributes); + retrievedContext = /** @type {WebGLRenderingContext} */ ( + newCanvas.getContext(contextType, opt_attributes)); // If context type is unsupported, return null. if (!retrievedContext) { diff --git a/src/wtf/replay/graphics/highlightvisualizer.js b/src/wtf/replay/graphics/highlightvisualizer.js index 9b6ec7df..a4d87919 100644 --- a/src/wtf/replay/graphics/highlightvisualizer.js +++ b/src/wtf/replay/graphics/highlightvisualizer.js @@ -41,10 +41,7 @@ goog.inherits(wtf.replay.graphics.HighlightVisualizer, /** - * Returns whether the visualization for a target substep is stored. - * @param {number} targetSubStepIndex Target substep. - * @return {boolean} Whether the visualization is stored for a target substep. - * @protected + * @override */ wtf.replay.graphics.HighlightVisualizer.prototype.visualizationStored = function(targetSubStepIndex) { diff --git a/src/wtf/replay/graphics/offscreensurface.js b/src/wtf/replay/graphics/offscreensurface.js index a61ef26f..370cd897 100644 --- a/src/wtf/replay/graphics/offscreensurface.js +++ b/src/wtf/replay/graphics/offscreensurface.js @@ -121,9 +121,9 @@ wtf.replay.graphics.OffscreenSurface = function(gl, width, height, opt_args) { * Internal texture. Used as a render target for this.framebuffer_. * Used by drawTexture and captureTexture. * @type {WebGLTexture} - * @private + * @protected */ - this.texture_ = null; + this.texture = null; /** * Renderbuffer used for depth/stencil information in the framebuffer. @@ -159,9 +159,9 @@ wtf.replay.graphics.OffscreenSurface = function(gl, width, height, opt_args) { /** * A buffer containing vertex positions arranged in a square. * @type {WebGLBuffer} - * @private + * @protected */ - this.squareVertexPositionBuffer_ = null; + this.squareVertexPositionBuffer = null; /** * A buffer containing texture coordinates arranged for a square. @@ -198,10 +198,10 @@ wtf.replay.graphics.OffscreenSurface.prototype.clearWebGLObjects = function() { var gl = this.context; gl.deleteFramebuffer(this.framebuffer_); - gl.deleteTexture(this.texture_); + gl.deleteTexture(this.texture); gl.deleteProgram(this.drawTextureProgram_); gl.deleteRenderbuffer(this.depthStencilBuffer_); - gl.deleteBuffer(this.squareVertexPositionBuffer_); + gl.deleteBuffer(this.squareVertexPositionBuffer); gl.deleteBuffer(this.squareTextureCoordBuffer_); }; @@ -234,8 +234,8 @@ wtf.replay.graphics.OffscreenSurface.prototype.initialize = function() { gl.bindFramebuffer(goog.webgl.FRAMEBUFFER, this.framebuffer_); // Create the texture and set it as a render target for the framebuffer. - this.texture_ = gl.createTexture(); - gl.bindTexture(goog.webgl.TEXTURE_2D, this.texture_); + this.texture = gl.createTexture(); + gl.bindTexture(goog.webgl.TEXTURE_2D, this.texture); gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MAG_FILTER, goog.webgl.LINEAR); gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MIN_FILTER, @@ -247,7 +247,7 @@ wtf.replay.graphics.OffscreenSurface.prototype.initialize = function() { gl.texImage2D(goog.webgl.TEXTURE_2D, 0, goog.webgl.RGBA, this.width, this.height, 0, goog.webgl.RGBA, goog.webgl.UNSIGNED_BYTE, null); gl.framebufferTexture2D(goog.webgl.FRAMEBUFFER, - goog.webgl.COLOR_ATTACHMENT0, goog.webgl.TEXTURE_2D, this.texture_, 0); + goog.webgl.COLOR_ATTACHMENT0, goog.webgl.TEXTURE_2D, this.texture, 0); // Set renderbuffer attachment and storage formats. if (this.depth_ && this.stencil_) { @@ -309,8 +309,8 @@ wtf.replay.graphics.OffscreenSurface.prototype.initialize = function() { gl.deleteShader(drawTextureFragmentShader); // Setup attributes aVertexPosition and aTextureCoord. - this.squareVertexPositionBuffer_ = gl.createBuffer(); - gl.bindBuffer(goog.webgl.ARRAY_BUFFER, this.squareVertexPositionBuffer_); + this.squareVertexPositionBuffer = gl.createBuffer(); + gl.bindBuffer(goog.webgl.ARRAY_BUFFER, this.squareVertexPositionBuffer); var vertices = [ -1.0, -1.0, 1.0, -1.0, @@ -399,7 +399,7 @@ wtf.replay.graphics.OffscreenSurface.prototype.resize = function( this.webGLState.backup(); - gl.bindTexture(goog.webgl.TEXTURE_2D, this.texture_); + gl.bindTexture(goog.webgl.TEXTURE_2D, this.texture); gl.texImage2D(goog.webgl.TEXTURE_2D, 0, goog.webgl.RGBA, this.width, this.height, 0, goog.webgl.RGBA, goog.webgl.UNSIGNED_BYTE, null); @@ -432,7 +432,7 @@ wtf.replay.graphics.OffscreenSurface.prototype.captureTexture = function() { var originalTextureBinding = /** @type {!WebGLTexture} */ ( gl.getParameter(goog.webgl.TEXTURE_BINDING_2D)); - gl.bindTexture(goog.webgl.TEXTURE_2D, this.texture_); + gl.bindTexture(goog.webgl.TEXTURE_2D, this.texture); var alpha = this.contextAttributes_['alpha']; var format = alpha ? goog.webgl.RGBA : goog.webgl.RGB; gl.copyTexImage2D(goog.webgl.TEXTURE_2D, 0, format, 0, 0, @@ -478,7 +478,7 @@ wtf.replay.graphics.OffscreenSurface.prototype.clear = function(opt_color) { wtf.replay.graphics.OffscreenSurface.prototype.drawTexture = function( opt_blend) { this.ensureInitialized(); - this.drawTextureInternal(this.texture_, this.drawTextureProgram_, opt_blend); + this.drawTextureInternal(this.texture, this.drawTextureProgram_, opt_blend); }; @@ -510,7 +510,7 @@ wtf.replay.graphics.OffscreenSurface.prototype.drawTextureInternal = function( // Update vertex attrib settings. var vertexAttribLocation = gl.getAttribLocation(program, 'aVertexPosition'); - gl.bindBuffer(goog.webgl.ARRAY_BUFFER, this.squareVertexPositionBuffer_); + gl.bindBuffer(goog.webgl.ARRAY_BUFFER, this.squareVertexPositionBuffer); gl.enableVertexAttribArray(vertexAttribLocation); gl.vertexAttribPointer(vertexAttribLocation, 2, goog.webgl.FLOAT, false, 0, 0); diff --git a/src/wtf/replay/graphics/overdrawsurface.js b/src/wtf/replay/graphics/overdrawsurface.js index b4ccc1c0..ab0d9bb4 100644 --- a/src/wtf/replay/graphics/overdrawsurface.js +++ b/src/wtf/replay/graphics/overdrawsurface.js @@ -118,9 +118,7 @@ wtf.replay.graphics.OverdrawSurface.prototype.getThresholdDrawColor = /** - * Creates framebuffer, texture, drawTextureProgram, and buffers. - * @return {boolean} Whether initialization succeeded. - * @protected + * @override */ wtf.replay.graphics.OverdrawSurface.prototype.initialize = function() { if (!goog.base(this, 'initialize')) { @@ -222,7 +220,7 @@ wtf.replay.graphics.OverdrawSurface.prototype.initialize = function() { wtf.replay.graphics.OffscreenSurface.prototype.drawOverdraw = function( opt_blend) { this.ensureInitialized(); - this.drawTextureInternal(this.texture_, this.drawOverdrawProgram_, opt_blend); + this.drawTextureInternal(this.texture, this.drawOverdrawProgram_, opt_blend); }; @@ -248,7 +246,7 @@ wtf.replay.graphics.OffscreenSurface.prototype.drawQuad = function() { // Update vertex attrib settings. var vertexAttribLocation = gl.getAttribLocation(this.drawQuadProgram_, 'aVertexPosition'); - gl.bindBuffer(goog.webgl.ARRAY_BUFFER, this.squareVertexPositionBuffer_); + gl.bindBuffer(goog.webgl.ARRAY_BUFFER, this.squareVertexPositionBuffer); gl.enableVertexAttribArray(vertexAttribLocation); gl.vertexAttribPointer(vertexAttribLocation, 2, goog.webgl.FLOAT, false, 0, 0); diff --git a/src/wtf/replay/graphics/program.js b/src/wtf/replay/graphics/program.js index 952d5660..162223ab 100644 --- a/src/wtf/replay/graphics/program.js +++ b/src/wtf/replay/graphics/program.js @@ -171,7 +171,7 @@ wtf.replay.graphics.Program.prototype.deleteVariants = function() { /** * Returns the original program. - * @return {WebGLProgram} + * @return {!WebGLProgram} */ wtf.replay.graphics.Program.prototype.getOriginalProgram = function() { return this.originalProgram_; diff --git a/src/wtf/replay/graphics/skipcallsvisualizer.js b/src/wtf/replay/graphics/skipcallsvisualizer.js index dea30596..728d420f 100644 --- a/src/wtf/replay/graphics/skipcallsvisualizer.js +++ b/src/wtf/replay/graphics/skipcallsvisualizer.js @@ -132,8 +132,7 @@ wtf.replay.graphics.SkipCallsVisualizer.prototype.getState = function() { /** - * Sets playback-affecting state. - * @param {wtf.replay.graphics.Visualizer.State} state The new state. + * @override */ wtf.replay.graphics.SkipCallsVisualizer.prototype.setState = function(state) { for (var i = 0; i < this.skippedProgramHandles_.length; ++i) { diff --git a/src/wtf/replay/graphics/ui/BUILD b/src/wtf/replay/graphics/ui/BUILD new file mode 100644 index 00000000..aefd2f7c --- /dev/null +++ b/src/wtf/replay/graphics/ui/BUILD @@ -0,0 +1,76 @@ +# Description: +# Graphics replay UI. + +package(default_visibility = ["//:internal"]) + +load("//builddefs:config.bzl", "COMMON_JS_SUPPRESSIONS") +load("//builddefs:less_rules.bzl", "less_css_library") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_template_library") + +closure_js_library( + name = "ui", + srcs = [ + "argumentsdialog.js", + "canvasarea.js", + "contextbox.js", + "eventnavigator.js", + "eventnavigatorsource.js", + "eventnavigatortoolbar.js", + "graphicspanel.js", + "graphicstoolbar.js", + "rangeseeker.js", + "replayframepainter.js", + ], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + ":ui_templates", + "//src/wtf/db", + "//src/wtf/events", + "//src/wtf/events:eventemitter", + "//src/wtf/math", + "//src/wtf/replay/graphics:graphics_library", + "//src/wtf/timing", + "//src/wtf/ui:control", + "//src/wtf/ui:dialog", + "//src/wtf/ui:errordialog", + "//src/wtf/ui:searchcontrol", + "//src/wtf/ui:tooltip", + "//src/wtf/ui:virtualtable", + "@io_bazel_rules_closure//closure/library", + ], +) + +closure_js_template_library( + name = "ui_templates", + srcs = [ + "argumentsdialog.soy", + "canvasarea.soy", + "contextbox.soy", + "eventnavigator.soy", + "eventnavigatortoolbar.soy", + "graphicspanel.soy", + "graphicstoolbar.soy", + "rangeseeker.soy", + ], +) + +less_css_library( + name = "graphics_styles", + srcs = [ + "argumentsdialog.less", + "canvasarea.less", + "contextbox.less", + "eventnavigator.less", + "eventnavigatortoolbar.less", + "graphicspanel.less", + "graphicstoolbar.less", + "rangeseeker.less", + ], + includes = ["src"], + main_srcs = ["graphics.less"], + deps = [ + "//src/wtf/ui:ui_styles", + "//src/wtf/ui/styles", + ], +) diff --git a/src/wtf/replay/graphics/ui/argumentsdialog.js b/src/wtf/replay/graphics/ui/argumentsdialog.js index 27081267..0777b532 100644 --- a/src/wtf/replay/graphics/ui/argumentsdialog.js +++ b/src/wtf/replay/graphics/ui/argumentsdialog.js @@ -121,9 +121,9 @@ wtf.replay.graphics.ui.ArgumentsDialog.prototype.createForm_ = function() { // Save the new arguments when either the update button is clicked or Enter // is hit. - eventHandler.listen(saveButton, goog.events.EventType.CLICK, + eventHandler.listenWithScope(saveButton, goog.events.EventType.CLICK, saveFunction, false, this); - eventHandler.listen(form, goog.events.EventType.SUBMIT, + eventHandler.listenWithScope(form, goog.events.EventType.SUBMIT, saveFunction, false, this); // Create label/input pair elements based on the type of value. @@ -138,7 +138,7 @@ wtf.replay.graphics.ui.ArgumentsDialog.prototype.createForm_ = function() { // Create the reset arguments button. var resetArgumentsElement = domHelper.getElementByClass( goog.getCssName('resetArgumentsButton')); - eventHandler.listen(resetArgumentsElement, + eventHandler.listenWithScope(resetArgumentsElement, goog.events.EventType.CLICK, function() { this.resetArguments_(); }, false, this); @@ -146,7 +146,7 @@ wtf.replay.graphics.ui.ArgumentsDialog.prototype.createForm_ = function() { // Create the cancel button. var cancelButton = domHelper.getElementByClass( goog.getCssName('cancelButton'), this.getRootElement()); - eventHandler.listen(cancelButton, + eventHandler.listenWithScope(cancelButton, goog.events.EventType.CLICK, function() { this.close(); }, false, this); diff --git a/src/wtf/replay/graphics/ui/canvasarea.js b/src/wtf/replay/graphics/ui/canvasarea.js index e50d9743..1a54d9a4 100644 --- a/src/wtf/replay/graphics/ui/canvasarea.js +++ b/src/wtf/replay/graphics/ui/canvasarea.js @@ -301,7 +301,7 @@ wtf.replay.graphics.ui.CanvasesArea.prototype.removeContext_ = function( /** - * Lays out the area where the canvases are displayed. + * @override */ wtf.replay.graphics.ui.CanvasesArea.prototype.layout = function() { if (this.resizeCanvasesToFit_) { diff --git a/src/wtf/replay/graphics/ui/eventnavigator.js b/src/wtf/replay/graphics/ui/eventnavigator.js index 1bc3ba89..0b344efa 100644 --- a/src/wtf/replay/graphics/ui/eventnavigator.js +++ b/src/wtf/replay/graphics/ui/eventnavigator.js @@ -107,7 +107,7 @@ wtf.replay.graphics.ui.EventNavigator.prototype.setReady = function() { /** - * Changes the layout of elements to fit the container. + * @override */ wtf.replay.graphics.ui.EventNavigator.prototype.layout = function() { this.table_.layout(); diff --git a/src/wtf/replay/graphics/ui/eventnavigatorsource.js b/src/wtf/replay/graphics/ui/eventnavigatorsource.js index 366f8497..b68c1e15 100644 --- a/src/wtf/replay/graphics/ui/eventnavigatorsource.js +++ b/src/wtf/replay/graphics/ui/eventnavigatorsource.js @@ -662,7 +662,7 @@ wtf.replay.graphics.ui.EventNavigatorTableSource.prototype.playbackToCurrent_ = * Gets the context handle of the given event. * This is slow and should be used sparingly. * @param {!wtf.db.EventIterator} it Iterator. - * @return {string?} Context handle, if any. + * @return {string} Context handle, if any. * @private */ wtf.replay.graphics.ui.EventNavigatorTableSource.prototype.getContextOfEvent_ = diff --git a/src/wtf/replay/graphics/ui/eventnavigatortoolbar.js b/src/wtf/replay/graphics/ui/eventnavigatortoolbar.js index 6822d8ab..27b85e8b 100644 --- a/src/wtf/replay/graphics/ui/eventnavigatortoolbar.js +++ b/src/wtf/replay/graphics/ui/eventnavigatortoolbar.js @@ -224,23 +224,23 @@ wtf.replay.graphics.ui.EventNavigatorToolbar.prototype.setReady = function() { // Handle button clicks. var eh = this.getHandler(); - eh.listen( + eh.listenWithScope( this.firstCallButton_, goog.events.EventType.CLICK, this.firstCallHandler_, false, this); - eh.listen( + eh.listenWithScope( this.previousDrawCallButton_, goog.events.EventType.CLICK, this.previousDrawCallHandler_, false, this); - eh.listen( + eh.listenWithScope( this.nextDrawCallButton_, goog.events.EventType.CLICK, this.nextDrawCallHandler_, false, this); - eh.listen( + eh.listenWithScope( this.lastCallButton_, goog.events.EventType.CLICK, this.lastCallHandler_, false, this); - eh.listen( + eh.listenWithScope( this.toggleOverdrawButton_, goog.events.EventType.CLICK, this.toggleOverdrawHandler_, false, this); diff --git a/src/wtf/replay/graphics/ui/graphicspanel.js b/src/wtf/replay/graphics/ui/graphicspanel.js index 98b3aa51..2d026a6a 100644 --- a/src/wtf/replay/graphics/ui/graphicspanel.js +++ b/src/wtf/replay/graphics/ui/graphicspanel.js @@ -66,7 +66,7 @@ wtf.replay.graphics.ui.GraphicsPanel = function( * @private */ this.viewportSizeMonitor_ = wtf.events.acquireViewportSizeMonitor(); - this.getHandler().listen( + this.getHandler().listenWithScope( this.viewportSizeMonitor_, goog.events.EventType.RESIZE, this.layout, false, this); @@ -138,7 +138,7 @@ wtf.replay.graphics.ui.GraphicsPanel = function( */ this.toggleResizeCanvasesButton_ = this.getChildElement( goog.getCssName('resizeCanvasesToFitButton')); - this.getHandler().listen( + this.getHandler().listenWithScope( this.toggleResizeCanvasesButton_, goog.events.EventType.CLICK, this.toggleResizeCanvases_, false, this); diff --git a/src/wtf/replay/graphics/ui/graphicstoolbar.js b/src/wtf/replay/graphics/ui/graphicstoolbar.js index 6c8297fc..b945e90a 100644 --- a/src/wtf/replay/graphics/ui/graphicstoolbar.js +++ b/src/wtf/replay/graphics/ui/graphicstoolbar.js @@ -153,23 +153,23 @@ wtf.replay.graphics.ui.GraphicsToolbar.prototype.setReady_ = // Handle button clicks. var eh = this.getHandler(); - eh.listen( + eh.listenWithScope( this.resetButton_, goog.events.EventType.CLICK, this.resetClickHandler_, false, this); - eh.listen( + eh.listenWithScope( this.backButton_, goog.events.EventType.CLICK, this.backClickHandler_, false, this); - eh.listen( + eh.listenWithScope( this.playButton_, goog.events.EventType.CLICK, this.playClickHandler_, false, this); - eh.listen( + eh.listenWithScope( this.forwardButton_, goog.events.EventType.CLICK, this.forwardClickHandler_, false, this); - eh.listen( + eh.listenWithScope( this.resetVisualizersButton_, goog.events.EventType.CLICK, this.resetVisualizersClickHandler_, false, this); diff --git a/src/wtf/replay/graphics/ui/rangeseeker.js b/src/wtf/replay/graphics/ui/rangeseeker.js index 9fc1a0f8..cd77ffae 100644 --- a/src/wtf/replay/graphics/ui/rangeseeker.js +++ b/src/wtf/replay/graphics/ui/rangeseeker.js @@ -197,7 +197,7 @@ wtf.replay.graphics.ui.RangeSeeker.prototype.createValueDisplayer_ = valueDisplayer.type = 'text'; // Update the seeker if displayer changes. - this.getHandler().listen(valueDisplayer, + this.getHandler().listenWithScope(valueDisplayer, goog.events.EventType.CHANGE, function() { var newValue = goog.string.parseInt(valueDisplayer.value); diff --git a/src/wtf/replay/graphics/ui/replayframepainter.js b/src/wtf/replay/graphics/ui/replayframepainter.js index 160d522c..4e476d6d 100644 --- a/src/wtf/replay/graphics/ui/replayframepainter.js +++ b/src/wtf/replay/graphics/ui/replayframepainter.js @@ -13,6 +13,7 @@ goog.provide('wtf.replay.graphics.ui.ReplayFramePainter'); +goog.require('goog.asserts'); goog.require('goog.dom.classes'); goog.require('wtf.events'); goog.require('wtf.math'); @@ -318,6 +319,7 @@ wtf.replay.graphics.ui.ReplayFramePainter.prototype.repaintInternal = function( var experiment, frames, frame; // Draw a colored bar graph for the currently selected experiment. + goog.asserts.assert(this.selectedExperiment_ !== null); experiment = experiments[this.selectedExperiment_]; if (experiment && experiment.getFrames()) { frames = experiment.getFrames(); diff --git a/src/wtf/replay/timetravel/BUILD b/src/wtf/replay/timetravel/BUILD new file mode 100644 index 00000000..a6130367 --- /dev/null +++ b/src/wtf/replay/timetravel/BUILD @@ -0,0 +1,54 @@ +# Description: +# Time-travel replay. + +package(default_visibility = ["//:internal"]) + +load("//builddefs:config.bzl", "COMMON_JS_SUPPRESSIONS") +load("//builddefs:less_rules.bzl", "less_css_library") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_template_library") + +closure_js_library( + name = "timetravel", + srcs = [ + "controller.js", + "exports.js", + "replaysession.js", + "timetravel.js", + ], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + ":controller_template", + "//src/wtf", + "//src/wtf/data", + "//src/wtf/db", + "//src/wtf/events:eventemitter", + "//src/wtf/io", + "//src/wtf/io/cff", + "//src/wtf/math", + "//src/wtf/ui:control", + "//src/wtf/ui:dialog", + "//src/wtf/ui:errordialog", + "//src/wtf/ui:progressdialog", + "//src/wtf/util", + "@io_bazel_rules_closure//closure/library", + ], +) + +closure_js_template_library( + name = "controller_template", + srcs = ["controller.soy"], +) + +less_css_library( + name = "timetravel_styles", + srcs = [ + "controller.less", + ], + includes = ["src"], + main_srcs = ["timetravel.less"], + deps = [ + "//src/wtf/ui:ui_styles", + "//src/wtf/ui/styles", + ], +) diff --git a/src/wtf/replay/timetravel/replaysession.js b/src/wtf/replay/timetravel/replaysession.js index 8c4731b7..cf0b9662 100644 --- a/src/wtf/replay/timetravel/replaysession.js +++ b/src/wtf/replay/timetravel/replaysession.js @@ -290,7 +290,7 @@ wtf.replay.timeTravel.ReplaySession.prototype.neuterPageEvents_ = function() { e.preventDefault(); e.stopPropagation(); return false; - }; + } for (var eventName in allEventTypes) { pageGlobal.addEventListener(eventName, eventDisabler, true); } diff --git a/src/wtf/timing/BUILD b/src/wtf/timing/BUILD new file mode 100644 index 00000000..10a5ec21 --- /dev/null +++ b/src/wtf/timing/BUILD @@ -0,0 +1,24 @@ +# Description: +# Timers and time queries. + +package(default_visibility = ["//:internal"]) + +load("//builddefs:config.bzl", "COMMON_JS_SUPPRESSIONS") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library") + +closure_js_library( + name = "timing", + srcs = [ + "browserinterval.js", + "handle.js", + "renderinterval.js", + "rendertimer.js", + "timing.js", + "util.js", + ], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + "//src/wtf", + "@io_bazel_rules_closure//closure/library", + ], +) diff --git a/src/wtf/timing/util.js b/src/wtf/timing/util.js index 0fa296ee..c7881b27 100644 --- a/src/wtf/timing/util.js +++ b/src/wtf/timing/util.js @@ -87,10 +87,11 @@ wtf.timing.util.getRequestAnimationFrame = function(opt_enableFallback) { * @param {Function} callback Callback to issue on tick. * @return {number} Cancellation handle. */ - return function(callback) { + function setTimeoutShim(callback) { return setTimeout.call( goog.global, callback, 1000 / wtf.timing.util.FRAMERATE); - }; + } + return setTimeoutShim; } return null; @@ -128,9 +129,10 @@ wtf.timing.util.getCancelAnimationFrame = * @param {number} id The result of a previous {@code requestAnimationFrame} * call. */ - return function(id) { + function clearTimeoutShim(id) { clearTimeout(id); - }; + } + return clearTimeoutShim; } return null; diff --git a/src/wtf/trace/BUILD b/src/wtf/trace/BUILD new file mode 100644 index 00000000..eb7c3124 --- /dev/null +++ b/src/wtf/trace/BUILD @@ -0,0 +1,82 @@ +# Description: +# Tracing support. + +package(default_visibility = ["//:internal"]) + +load("//builddefs:config.bzl", "COMMON_JS_SUPPRESSIONS") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library") + +closure_js_library( + name = "trace", + srcs = [ + "builtinevents.js", + "eventregistry.js", + "events.js", + "eventsessioncontext.js", + "eventtarget.js", + "eventtype.js", + "eventtypebuilder.js", + "exports.js", + "flow.js", + "instrument.js", + "prepare.js", + "provider.js", + "scope.js", + "session.js", + "trace.js", + "tracemanager.js", + "zone.js", + + # TODO(benvanik): conditional on build type? + "providers/chromedebugprovider.js", + "providers/consoleprovider.js", + "providers/domprovider.js", + "providers/firefoxdebugprovider.js", + "providers/imageprovider.js", + "providers/providers.js", + "providers/replayprovider.js", + "providers/timingprovider.js", + "providers/webglprovider.js", + "providers/websocketprovider.js", + "providers/webworkerprovider.js", + "providers/xhrprovider.js", + "sessions/nullsession.js", + "sessions/snapshottingsession.js", + "sessions/streamingsession.js", + ], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + ":util", + "//src/wtf", + "//src/wtf/data", + "//src/wtf/events:eventemitter", + "//src/wtf/io", + "//src/wtf/io/cff", + "//src/wtf/ipc", + "//src/wtf/math", + "//src/wtf/pal", + "//src/wtf/timing", + "//src/wtf/util", + "@io_bazel_rules_closure//closure/library", + ], +) + +closure_js_library( + name = "node", + srcs = ["node.js"], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + ":trace", + "//src/wtf", + ], +) + +closure_js_library( + name = "util", + srcs = ["util.js"], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + "//src/wtf", + "@io_bazel_rules_closure//closure/library", + ], +) diff --git a/src/wtf/trace/events.js b/src/wtf/trace/events.js index e812bd49..2f96373e 100644 --- a/src/wtf/trace/events.js +++ b/src/wtf/trace/events.js @@ -13,6 +13,7 @@ goog.provide('wtf.trace.events'); +goog.require('goog.asserts'); goog.require('wtf.data.EventClass'); goog.require('wtf.data.Variable'); goog.require('wtf.trace.EventRegistry'); @@ -24,7 +25,7 @@ goog.require('wtf.trace.EventType'); * @param {string} signature Event signature. * @param {wtf.data.EventClass} eventClass Event class. * @param {number} flags A bitmask of {@see wtf.data.EventFlag} values. - * @return {wtf.trace.EventType} New event type. + * @return {!wtf.trace.EventType} New event type. * @private */ wtf.trace.events.create_ = function(signature, eventClass, flags) { @@ -89,6 +90,7 @@ wtf.trace.events.create_ = function(signature, eventClass, flags) { wtf.trace.events.createInstance = function(signature, opt_flags) { var eventType = wtf.trace.events.create_( signature, wtf.data.EventClass.INSTANCE, opt_flags || 0); + goog.asserts.assert(eventType.append); return eventType.append; }; @@ -129,5 +131,6 @@ wtf.trace.events.createInstance = function(signature, opt_flags) { wtf.trace.events.createScope = function(signature, opt_flags) { var eventType = wtf.trace.events.create_( signature, wtf.data.EventClass.SCOPE, opt_flags || 0); + goog.asserts.assert(eventType.append); return eventType.append; }; diff --git a/src/wtf/trace/eventtype.js b/src/wtf/trace/eventtype.js index 0ecafa23..ad9b26c2 100644 --- a/src/wtf/trace/eventtype.js +++ b/src/wtf/trace/eventtype.js @@ -103,8 +103,7 @@ wtf.trace.EventType.nextEventWireId_ = 1; /** - * Gets a pretty-formatted name for the event. - * @return {string} Pretty-formatted name. + * @override */ wtf.trace.EventType.prototype.toString = function() { return this.name; diff --git a/src/wtf/trace/eventtypebuilder.js b/src/wtf/trace/eventtypebuilder.js index 7f8cd6da..cb93ffa9 100644 --- a/src/wtf/trace/eventtypebuilder.js +++ b/src/wtf/trace/eventtypebuilder.js @@ -46,7 +46,7 @@ goog.inherits(wtf.trace.EventTypeBuilder, wtf.util.FunctionBuilder); * Generates an event tracing function. * @param {!wtf.trace.EventSessionContextType} context Event session context. * @param {!wtf.trace.EventType} eventType Event type. - * @return {Function} Generated function based on class. + * @return {!Function} Generated function based on class. */ wtf.trace.EventTypeBuilder.prototype.generate = function(context, eventType) { var writers = wtf.trace.EventTypeBuilder.WRITERS_; diff --git a/src/wtf/trace/instrument.js b/src/wtf/trace/instrument.js index 49af3706..22ff5760 100644 --- a/src/wtf/trace/instrument.js +++ b/src/wtf/trace/instrument.js @@ -41,7 +41,7 @@ goog.require('wtf.util'); * event type. * @param {(function())=} opt_pre Code to execute before the scope is entered. * This is only called if {@code opt_generator} is not provided. - * @return {Function} The instrumented input value. + * @return {!Function} The instrumented input value. */ wtf.trace.instrument = function(value, signature, opt_namePrefix, opt_generator, opt_pre) { @@ -128,14 +128,14 @@ wtf.trace.instrument = function(value, signature, opt_namePrefix, * @param {Object|!Object.} methodMap A map of translated method names * to method signatures. Only the methods in this map will be * auto-instrumented. - * @return {Function} The instrumented input value. + * @return {!Function} The instrumented input value. */ wtf.trace.instrumentType = function(value, constructorSignature, methodMap) { // Rewrite constructor. This requires changing the entire type, which is why // we return the result. var newValue = wtf.trace.instrument(value, constructorSignature); /** @constructor */ - function tempCtor() {}; + function tempCtor() {} tempCtor.prototype = value.prototype; newValue.superClass_ = value.prototype; newValue.prototype = new tempCtor(); diff --git a/src/wtf/trace/providers/chromedebugprovider.js b/src/wtf/trace/providers/chromedebugprovider.js index 71ae319e..f35cc028 100644 --- a/src/wtf/trace/providers/chromedebugprovider.js +++ b/src/wtf/trace/providers/chromedebugprovider.js @@ -642,7 +642,7 @@ wtf.trace.providers.ChromeDebugProvider.prototype.toggleCapture_ = function() { consoleTimeEnd.call(goog.global.console, syncName); wtf.trace.leaveScope(tracingScope); - }; + } if (!this.isCapturingTracing_) { // Start tracing. diff --git a/src/wtf/trace/providers/webglprovider.js b/src/wtf/trace/providers/webglprovider.js index af6f730c..b20eccd5 100644 --- a/src/wtf/trace/providers/webglprovider.js +++ b/src/wtf/trace/providers/webglprovider.js @@ -350,7 +350,7 @@ wtf.trace.providers.WebGLProvider.prototype.injectContextType_ = function() { getHandle(ctx), ctx.drawingBufferWidth, ctx.drawingBufferHeight); } - }; + } // Stash off functions that let us quickly restore the context prototype // to its original state. @@ -410,7 +410,7 @@ wtf.trace.providers.WebGLProvider.prototype.injectContextType_ = function() { contextRestoreFns.push(function() { target[methodName] = rawFn; }); - }; + } /** * @param {string} signature Event signature. @@ -420,7 +420,7 @@ wtf.trace.providers.WebGLProvider.prototype.injectContextType_ = function() { wrapMethod( WebGLRenderingContext.prototype, 'WebGLRenderingContext', signature, opt_generator); - }; + } function coercePixelTypeToUint8(source) { if (!(source instanceof Uint8Array)) { @@ -433,7 +433,7 @@ wtf.trace.providers.WebGLProvider.prototype.injectContextType_ = function() { } else { return source; } - }; + } /** * Wraps the ANGLEInstancedArrays extension object. @@ -449,7 +449,7 @@ wtf.trace.providers.WebGLProvider.prototype.injectContextType_ = function() { wrapMethod( proto, 'ANGLEInstancedArrays', signature, opt_generator); - }; + } wrapInstancedArraysMethod( 'drawArraysInstancedANGLE(uint32 mode, uint32 first, int32 count, ' + @@ -480,7 +480,7 @@ wtf.trace.providers.WebGLProvider.prototype.injectContextType_ = function() { return leaveScope(scope, fn.apply(this, arguments)); }; }); - }; + } /** * Wraps the OESVertexArrayObject extension object. @@ -496,7 +496,7 @@ wtf.trace.providers.WebGLProvider.prototype.injectContextType_ = function() { wrapMethod( proto, 'OESVertexArrayObject', signature, opt_generator); - }; + } // http://www.khronos.org/registry/webgl/extensions/OES_vertex_array_object/ @@ -541,7 +541,7 @@ wtf.trace.providers.WebGLProvider.prototype.injectContextType_ = function() { return leaveScope(scope, fn.apply(this, arguments)); }; }); - }; + } /** * Wraps the WebGLLoseContext extension object. @@ -557,7 +557,7 @@ wtf.trace.providers.WebGLProvider.prototype.injectContextType_ = function() { wrapMethod( proto, 'WebGLLoseContext', signature, opt_generator); - }; + } // http://www.khronos.org/registry/webgl/extensions/WEBGL_lose_context/ @@ -579,7 +579,7 @@ wtf.trace.providers.WebGLProvider.prototype.injectContextType_ = function() { return leaveScope(scope, fn.apply(this, arguments)); }; }); - }; + } /** * Wraps an extension object. @@ -608,7 +608,7 @@ wtf.trace.providers.WebGLProvider.prototype.injectContextType_ = function() { delete proto['__gl_wrapped__']; }); return true; - }; + } switch (name) { case 'ANGLE_instanced_arrays': @@ -637,7 +637,7 @@ wtf.trace.providers.WebGLProvider.prototype.injectContextType_ = function() { default: return true; } - }; + } wrapContextMethod( 'getContextAttributes()'); @@ -861,7 +861,7 @@ wtf.trace.providers.WebGLProvider.prototype.injectContextType_ = function() { return obj; }; }); - }; + } wrapCreateMethod( 'createBuffer', 'buffer'); wrapCreateMethod( @@ -885,7 +885,7 @@ wtf.trace.providers.WebGLProvider.prototype.injectContextType_ = function() { return leaveScope(scope, fn.apply(this, arguments)); }; }); - }; + } wrapDeleteMethod( 'deleteBuffer', 'buffer'); wrapDeleteMethod( @@ -1096,7 +1096,7 @@ wtf.trace.providers.WebGLProvider.prototype.injectContextType_ = function() { return leaveScope(scope, fn.apply(this, arguments)); }; }); - }; + } wrapIsMethod('isBuffer'); wrapIsMethod('isFramebuffer'); wrapIsMethod('isProgram'); @@ -1332,7 +1332,7 @@ wtf.trace.providers.WebGLProvider.prototype.injectContextType_ = function() { }); break; } - }; + } function wrapUniformArrayMethod(name, type, count) { var signature = name + '(uint32 location, ' + type + '[] v)'; wrapContextMethod(signature, function(fn, eventType) { @@ -1342,7 +1342,7 @@ wtf.trace.providers.WebGLProvider.prototype.injectContextType_ = function() { return leaveScope(scope, fn.apply(this, arguments)); }; }); - }; + } function wrapUniformMatrixMethod(name, type, count) { var signature = name + '(uint32 location, uint8 transpose, ' + type + '[] value)'; @@ -1353,7 +1353,7 @@ wtf.trace.providers.WebGLProvider.prototype.injectContextType_ = function() { return leaveScope(scope, fn.apply(this, arguments)); }; }); - }; + } wrapUniformMethod( 'uniform1f', 'float', 1); wrapUniformMethod( diff --git a/src/wtf/trace/providers/websocketprovider.js b/src/wtf/trace/providers/websocketprovider.js index 70c55385..2b8cbb35 100644 --- a/src/wtf/trace/providers/websocketprovider.js +++ b/src/wtf/trace/providers/websocketprovider.js @@ -141,6 +141,7 @@ wtf.trace.providers.WebSocketProvider.prototype.injectWs_ = function() { ProxyWebSocket.prototype['CLOSED'] = 3; // Event tracking. + /** @override */ ProxyWebSocket.prototype.beginTrackingEvent = function(type) { var self = this; var tracker = function(e) { @@ -149,6 +150,7 @@ wtf.trace.providers.WebSocketProvider.prototype.injectWs_ = function() { this.trackers_[type] = tracker; this.handle_.addEventListener(type, tracker, false); }; + /** @override */ ProxyWebSocket.prototype.endTrackingEvent = function(type) { this.handle_.removeEventListener(type, this.trackers_[type], false); delete this.trackers_[type]; @@ -187,7 +189,7 @@ wtf.trace.providers.WebSocketProvider.prototype.injectWs_ = function() { this.handle_[name] = value; } }); - }; + } setupProxyProperty('url', true); setupProxyProperty('readyState'); diff --git a/src/wtf/trace/providers/webworkerprovider.js b/src/wtf/trace/providers/webworkerprovider.js index e1f56831..93888825 100644 --- a/src/wtf/trace/providers/webworkerprovider.js +++ b/src/wtf/trace/providers/webworkerprovider.js @@ -311,7 +311,7 @@ wtf.trace.providers.WebWorkerProvider.prototype.injectBrowserShim_ = var shimScriptUrl = goog.fs.createObjectUrl(shimBlob); return shimScriptUrl; - }; + } /** * Worker shim. @@ -397,6 +397,7 @@ wtf.trace.providers.WebWorkerProvider.prototype.injectBrowserShim_ = goog.inherits(ProxyWorker, wtf.trace.eventtarget.BaseEventTarget); // Event tracking. + /** @override */ ProxyWorker.prototype.beginTrackingEvent = function(type) { var self = this; var tracker = function(e) { @@ -405,6 +406,7 @@ wtf.trace.providers.WebWorkerProvider.prototype.injectBrowserShim_ = this.trackers_[type] = tracker; this.handle_.addEventListener(type, tracker, false); }; + /** @override */ ProxyWorker.prototype.endTrackingEvent = function(type) { this.handle_.removeEventListener(type, this.trackers_[type], false); delete this.trackers_[type]; @@ -645,5 +647,5 @@ wtf.trace.providers.WebWorkerProvider.prototype.injectProxyWorker_ = 'command': command, 'value': opt_value || null }, []); - }; + } }; diff --git a/src/wtf/trace/providers/xhrprovider.js b/src/wtf/trace/providers/xhrprovider.js index b6dc2b32..2afe05ba 100644 --- a/src/wtf/trace/providers/xhrprovider.js +++ b/src/wtf/trace/providers/xhrprovider.js @@ -206,6 +206,7 @@ wtf.trace.providers.XhrProvider.prototype.injectXhr_ = function() { ProxyXMLHttpRequest.prototype['DONE'] = 4; // Event tracking. + /** @override */ ProxyXMLHttpRequest.prototype.beginTrackingEvent = function(type) { var self = this; var tracker = function(e) { @@ -224,6 +225,7 @@ wtf.trace.providers.XhrProvider.prototype.injectXhr_ = function() { this.trackers_[type] = tracker; this.handle_.addEventListener(type, tracker, false); }; + /** @override */ ProxyXMLHttpRequest.prototype.endTrackingEvent = function(type) { this.handle_.removeEventListener(type, this.trackers_[type], false); delete this.trackers_[type]; @@ -262,18 +264,20 @@ wtf.trace.providers.XhrProvider.prototype.injectXhr_ = function() { this.handle_[name] = value; } }); - }; + } setupProxyProperty('readyState'); setupProxyProperty('timeout', true); setupProxyProperty('withCredentials', true); setupProxyProperty('upload'); + /** @override */ ProxyXMLHttpRequest.prototype['setRequestHeader'] = function( header, value) { var props = this.props_; props['headers'][header] = value; return this.handle_.setRequestHeader.apply(this.handle_, arguments); }; + /** @override */ ProxyXMLHttpRequest.prototype['overrideMimeType'] = function(mime) { var props = this.props_; props['overrideMimeType'] = mime; diff --git a/src/wtf/trace/tracemanager.js b/src/wtf/trace/tracemanager.js index 09c6bf0c..320ad6c2 100644 --- a/src/wtf/trace/tracemanager.js +++ b/src/wtf/trace/tracemanager.js @@ -461,7 +461,7 @@ wtf.trace.TraceManager.prototype.requestSnapshots = function( goog.dispose(streamTarget); goog.dispose(transport); callback.call(opt_scope, allBuffers.length ? allBuffers : null); - }; + } // Snapshot self. session.snapshot(streamTarget); @@ -481,7 +481,7 @@ wtf.trace.TraceManager.prototype.requestSnapshots = function( if (!pendingSnapshots) { complete(); } - }; + } for (var n = 0; n < this.listeners_.length; n++) { var requested = this.listeners_[n].requestSnapshots( session, listenerCallback); diff --git a/src/wtf/ui/BUILD b/src/wtf/ui/BUILD new file mode 100644 index 00000000..2aa43a27 --- /dev/null +++ b/src/wtf/ui/BUILD @@ -0,0 +1,246 @@ +# Description: +# Common UI utilities and support. +# App UI lives under //src/wtf/app. + +package(default_visibility = ["//:internal"]) + +load("//builddefs:config.bzl", "COMMON_JS_SUPPRESSIONS") +load("//builddefs:less_rules.bzl", "less_css_library") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_template_library") + +# TODO(benvanik): split up. +less_css_library( + name = "ui_styles", + srcs = [ + "dialog.less", + "errordialog.less", + "progressdialog.less", + "settingsdialog.less", + "tooltip.less", + "virtualtable.less", + ], + includes = ["src"], + main_srcs = ["ui.less"], + deps = [ + "//src/wtf/ui/styles", + ], +) + +closure_js_library( + name = "color", + srcs = [ + "color/color.js", + "color/palette.js", + "//third_party/d3:colorbrewer", + ], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + "@io_bazel_rules_closure//closure/library", + ], +) + +closure_js_library( + name = "control", + srcs = [ + "control.js", + "painter.js", + "resizablecontrol.js", + ], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + ":tooltip", + "//src/wtf/events", + "//src/wtf/events:eventemitter", + "//src/wtf/timing", + "//src/wtf/util", + "@io_bazel_rules_closure//closure/library", + ], +) + +closure_js_library( + name = "dialog", + srcs = [ + "dialog.js", + ], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + ":control", + "//src/wtf/events", + "//src/wtf/timing", + "@io_bazel_rules_closure//closure/library", + ], +) + +closure_js_library( + name = "errordialog", + srcs = ["errordialog.js"], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + ":dialog", + ":errordialog_template", + "@io_bazel_rules_closure//closure/library", + ], +) + +closure_js_template_library( + name = "errordialog_template", + srcs = ["errordialog.soy"], +) + +closure_js_library( + name = "icons", + srcs = ["icons.js"], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + "@io_bazel_rules_closure//closure/library", + ], +) + +closure_js_library( + name = "gridpainter", + srcs = ["gridpainter.js"], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + ":control", + ":timepainter", + "//src/wtf/math", + "@io_bazel_rules_closure//closure/library", + ], +) + +closure_js_library( + name = "progressdialog", + srcs = ["progressdialog.js"], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + ":dialog", + ":progressdialog_template", + "@io_bazel_rules_closure//closure/library", + ], +) + +closure_js_template_library( + name = "progressdialog_template", + srcs = ["progressdialog.soy"], +) + +closure_js_library( + name = "rangepainter", + srcs = [ + "rangepainter.js", + "rangerenderer.js", + ], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + ":control", + ":timepainter", + "//src/wtf/math", + "@io_bazel_rules_closure//closure/library", + ], +) + +closure_js_library( + name = "rulerpainter", + srcs = ["rulerpainter.js"], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + ":control", + ":timepainter", + "//src/wtf/db:unit", + "//src/wtf/math", + "@io_bazel_rules_closure//closure/library", + ], +) + +closure_js_library( + name = "searchcontrol", + srcs = ["searchcontrol.js"], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + ":control", + "//src/wtf/events", + "//src/wtf/events:eventemitter", + "@io_bazel_rules_closure//closure/library", + ], +) + +closure_js_library( + name = "settingsdialog", + srcs = ["settingsdialog.js"], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + ":dialog", + ":settingsdialog_template", + "//src/wtf/trace:util", + "@io_bazel_rules_closure//closure/library", + ], +) + +closure_js_template_library( + name = "settingsdialog_template", + srcs = ["settingsdialog.soy"], +) + +closure_js_library( + name = "timepainter", + srcs = ["timepainter.js"], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + ":control", + "//src/wtf/db:unit", + "@io_bazel_rules_closure//closure/library", + ], +) + +closure_js_library( + name = "tooltip", + srcs = ["tooltip.js"], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + "@io_bazel_rules_closure//closure/library", + ], +) + +closure_js_library( + name = "virtualtable", + srcs = [ + "virtualtable.js", + "virtualtablesource.js", + ], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + ":control", + ":tooltip", + ":virtualtable_template", + "//src/wtf/events", + "//src/wtf/events:eventemitter", + "//src/wtf/math", + "//src/wtf/timing", + "@io_bazel_rules_closure//closure/library", + ], +) + +closure_js_template_library( + name = "virtualtable_template", + srcs = ["virtualtable.soy"], +) + +closure_js_library( + name = "zoom", + srcs = [ + "zoom/element.js", + "zoom/itarget.js", + "zoom/spring.js", + "zoom/viewport.js", + "zoom/zoom.js", + ], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + "//src/wtf", + "//src/wtf/events", + "//src/wtf/events:eventemitter", + "//src/wtf/timing", + "@io_bazel_rules_closure//closure/library", + ], +) diff --git a/src/wtf/ui/color/color.js b/src/wtf/ui/color/color.js index 3e1166ba..087035d3 100644 --- a/src/wtf/ui/color/color.js +++ b/src/wtf/ui/color/color.js @@ -79,7 +79,7 @@ wtf.ui.color.RgbColor = function(r, g, b) { /** * Gets the #RRGGBB string. - * @return {string} Value. + * @override */ wtf.ui.color.RgbColor.prototype.toString = function() { return this.string_; diff --git a/src/wtf/ui/control.js b/src/wtf/ui/control.js index 7e8902ed..b332b345 100644 --- a/src/wtf/ui/control.js +++ b/src/wtf/ui/control.js @@ -331,7 +331,7 @@ wtf.ui.Control.prototype.toggleInputEvents_ = function(value) { modifiers |= wtf.ui.ModifierKey.META; } return modifiers; - }; + } eh.listen( canvas, diff --git a/src/wtf/ui/errordialog.js b/src/wtf/ui/errordialog.js index a00fdfa8..3cf85e81 100644 --- a/src/wtf/ui/errordialog.js +++ b/src/wtf/ui/errordialog.js @@ -49,7 +49,7 @@ wtf.ui.ErrorDialog = function(message, detail, opt_dom) { }, body, dom); var eh = this.getHandler(); - eh.listen( + eh.listenWithScope( this.getChildElement(goog.getCssName('buttonClose')), goog.events.EventType.CLICK, function() { this.close(); diff --git a/src/wtf/ui/errordialog.soy b/src/wtf/ui/errordialog.soy index c1afe8b3..a26118f3 100644 --- a/src/wtf/ui/errordialog.soy +++ b/src/wtf/ui/errordialog.soy @@ -19,7 +19,7 @@ * @param message Error message. * @param detail Detailed information. */ -{template .control} +{template .control autoescape="deprecated-contextual"}
diff --git a/src/wtf/ui/settingsdialog.js b/src/wtf/ui/settingsdialog.js index a60a2282..717db78d 100644 --- a/src/wtf/ui/settingsdialog.js +++ b/src/wtf/ui/settingsdialog.js @@ -81,14 +81,14 @@ wtf.ui.SettingsDialog = function(options, title, parentElement, opt_dom) { dom.setTextContent(titleEl, title); var eh = this.getHandler(); - eh.listen( + eh.listenWithScope( this.getChildElement(goog.getCssName('buttonSave')), goog.events.EventType.CLICK, function() { this.saveSettings(); this.close(); }, false, this); - eh.listen( + eh.listenWithScope( this.getChildElement(goog.getCssName('buttonCancel')), goog.events.EventType.CLICK, function() { this.close(); diff --git a/src/wtf/ui/styles/BUILD b/src/wtf/ui/styles/BUILD new file mode 100644 index 00000000..4f0114bc --- /dev/null +++ b/src/wtf/ui/styles/BUILD @@ -0,0 +1,23 @@ +# Description: +# Shared UI styles for reset, compatibility, and kennedy. + +package(default_visibility = ["//:internal"]) + +load("//builddefs:less_rules.bzl", "less_css_library") + +less_css_library( + name = "styles", + srcs = [ + "common.less", + "css3.less", + "kennedy.less", + "reset.less", + ], + includes = [ + ".", + "src", + ], + deps = [ + "//third_party:normalize_css", + ], +) diff --git a/src/wtf/ui/styles/common.less b/src/wtf/ui/styles/common.less index b2aa7bff..9b4d7828 100644 --- a/src/wtf/ui/styles/common.less +++ b/src/wtf/ui/styles/common.less @@ -11,10 +11,10 @@ .fitParent { position: absolute; - left: 0; - right: 0; - top: 0; - bottom: 0; + /* @alternate */ left: 0; + /* @alternate */ right: 0; + /* @alternate */ top: 0; + /* @alternate */ bottom: 0; } .fitParentRelative { position: relative; diff --git a/src/wtf/ui/styles/css3.less b/src/wtf/ui/styles/css3.less index abf07a00..1980acdc 100644 --- a/src/wtf/ui/styles/css3.less +++ b/src/wtf/ui/styles/css3.less @@ -22,15 +22,15 @@ .backgroundGradient(@topColor, @bottomColor) { background-color: @topColor; - background-image: -webkit-linear-gradient(top,@topColor,@bottomColor); - background-image: -moz-linear-gradient(top,@topColor,@bottomColor); - background-image: -ms-linear-gradient(top,@topColor,@bottomColor); - background-image: -o-linear-gradient(top,@topColor,@bottomColor); - background-image: linear-gradient(top,@topColor,@bottomColor); + /* @alternate */ background-image: -webkit-linear-gradient(top,@topColor,@bottomColor); + /* @alternate */ background-image: -moz-linear-gradient(top,@topColor,@bottomColor); + /* @alternate */ background-image: -ms-linear-gradient(top,@topColor,@bottomColor); + /* @alternate */ background-image: -o-linear-gradient(top,@topColor,@bottomColor); + /* @alternate */ background-image: linear-gradient(top,@topColor,@bottomColor); // IE8+: -ms-filter: %("progid:DXImageTransform.Microsoft.gradient(startColorstr=%d, endColorstr=%d)", @topColor, @bottomColor); // IE10: - background-image: -ms-linear-gradient(top,@topColor, @bottomColor); + /* @alternate */ background-image: -ms-linear-gradient(top,@topColor, @bottomColor); } .boxSizing(@sizing: border-box) { diff --git a/src/wtf/ui/styles/kennedy.less b/src/wtf/ui/styles/kennedy.less index 79d53073..6544146f 100644 --- a/src/wtf/ui/styles/kennedy.less +++ b/src/wtf/ui/styles/kennedy.less @@ -9,6 +9,9 @@ * @author benvanik@google.com (Ben Vanik) */ +@import "wtf/ui/styles/reset.less"; +@import "wtf/ui/styles/css3.less"; + .k { font-family: "Arial", "Helvetica", sans-serif; color: #222222; @@ -125,7 +128,6 @@ text-decoration: none; display: inline-block; min-width: 54px; - border: 1px solid #DCDCDC; border: 1px solid rgba(0,0,0,0.1); text-align: center; color: #444; @@ -183,7 +185,6 @@ &.kDisabled, &.kDisabled:hover, &.kDisabled:active { background: none; color: #b8b8b8; - border: 1px solid #f3f3f3; border: 1px solid rgba(0,0,0,0.05); cursor: default; pointer-events: none; @@ -232,12 +233,9 @@ } &.kDisabled, &.kDisabled:hover, &.kDisabled:active { - border: 1px solid #505050; - background-color: #666; - opacity: 0.5; - - border-color: #b0281a; + border: 1px solid #b0281a; background-color: #d14836; + opacity: 0.5; } } @@ -354,7 +352,7 @@ display: inline-block; width: 7px; height: 7px; - background: url('app/icons/arrow_dropdown.svg') center no-repeat; + background: url('icons/arrow_dropdown.svg') center no-repeat; .transition(all, 0.218s); } .kDisabled .kButtonDisclosure { @@ -482,15 +480,15 @@ padding-top:100px; background-clip:padding-box; background-color: rgba(0,0,0,0.2); - -webkit-box-shadow: inset 1px 1px 0px rgba(0,0,0,0.10), - inset 0px -1px 0px rgba(0,0,0,0.07); + .insetBoxShadow2(1px, 1px, 0px, rgba(0,0,0,0.10), + 0px, -1px, 0px, rgba(0,0,0,0.07)); } ::-webkit-scrollbar-thumb:hover { + .insetBoxShadow(1px, 1px, 1px, rgba(0,0,0,0.25)); background-color: rgba(0,0,0,0.4); - -webkit-box-shadow: inset 1px 1px 1px rgba(0,0,0,0.25); } ::-webkit-scrollbar-thumb:active { - -webkit-box-shadow: inset 1px 1px 3px rgba(0,0,0,0.35); + .insetBoxShadow(1px, 1px, 3px, rgba(0,0,0,0.35)); background-color: rgba(0,0,0,0.5); } ::-webkit-scrollbar-thumb:vertical { @@ -507,12 +505,12 @@ } ::-webkit-scrollbar-track:hover { background-color:rgba(0,0,0,0.05); - -webkit-box-shadow: inset 1px 0px 0px rgba(0,0,0,0.10); + .insetBoxShadow(1px, 0px, 0px, rgba(0,0,0,0.10)); } ::-webkit-scrollbar-track:active { background-color:rgba(0,0,0,0.05); - -webkit-box-shadow: inset 1px 0px 0px rgba(0,0,0,0.14), - inset -1px -1px 0px rgba(0,0,0,0.07); + .insetBoxShadow2(1px, 0px, 0px, rgba(0,0,0,0.14), + -1px, -1px, 0px, rgba(0,0,0,0.07)); } .kScrollBarInner::-webkit-scrollbar-track:vertical { border-left: 6px solid transparent; @@ -656,7 +654,7 @@ padding-left: 30px; } .goog-option-selected { - background-image: url('app/icons/checkmark.svg'); + background-image: url('icons/checkmark.svg'); background-position: left center; background-repeat: no-repeat; background-position-x: 5px; @@ -677,11 +675,11 @@ background-size: 20px 10px; background-color: rgba(0,0,0,0.12); - background-image: -webkit-linear-gradient(315deg, transparent, transparent 33%, rgba(0,0,0,0.12) 33%, rgba(0,0,0,0.12) 66%, transparent 66%, transparent); - background-image: -moz-linear-gradient(315deg, transparent, transparent 33%, rgba(0,0,0,0.12) 33%, rgba(0,0,0,0.12) 66%, transparent 66%, transparent); - background-image: -ms-linear-gradient(315deg, transparent, transparent 33%, rgba(0,0,0,0.12) 33%, rgba(0,0,0,0.12) 66%, transparent 66%, transparent); - background-image: -o-linear-gradient(315deg, transparent, transparent 33%, rgba(0,0,0,0.12) 33%, rgba(0,0,0,0.12) 66%, transparent 66%, transparent); - background-image: linear-gradient(315deg, transparent, transparent 33%, rgba(0,0,0,0.12) 33%, rgba(0,0,0,0.12) 66%, transparent 66%, transparent); + /* @alternate */ background-image: -webkit-linear-gradient(315deg, transparent, transparent 33%, rgba(0,0,0,0.12) 33%, rgba(0,0,0,0.12) 66%, transparent 66%, transparent); + /* @alternate */ background-image: -moz-linear-gradient(315deg, transparent, transparent 33%, rgba(0,0,0,0.12) 33%, rgba(0,0,0,0.12) 66%, transparent 66%, transparent); + /* @alternate */ background-image: -ms-linear-gradient(315deg, transparent, transparent 33%, rgba(0,0,0,0.12) 33%, rgba(0,0,0,0.12) 66%, transparent 66%, transparent); + /* @alternate */ background-image: -o-linear-gradient(315deg, transparent, transparent 33%, rgba(0,0,0,0.12) 33%, rgba(0,0,0,0.12) 66%, transparent 66%, transparent); + /* @alternate */ background-image: linear-gradient(315deg, transparent, transparent 33%, rgba(0,0,0,0.12) 33%, rgba(0,0,0,0.12) 66%, transparent 66%, transparent); .animation(kProgressBarAnimation, 0.8s, linear, infinite); } diff --git a/src/wtf/ui/styles/reset.less b/src/wtf/ui/styles/reset.less index 99e13ff8..7be873e8 100644 --- a/src/wtf/ui/styles/reset.less +++ b/src/wtf/ui/styles/reset.less @@ -36,7 +36,7 @@ blockquote, q { quotes: none; } blockquote:before, blockquote:after, q:before, q:after { - content: ''; + /* @alternate */ content: ''; content: none; } @@ -64,4 +64,4 @@ img { display: inline } /** * normalize.css */ -@import (less) "../../../../third_party/normalize.css/normalize.css"; +@import (less) "third_party/normalize.css/normalize.css"; diff --git a/src/wtf/ui/virtualtable.less b/src/wtf/ui/virtualtable.less index 6cf1048d..eef1aadf 100644 --- a/src/wtf/ui/virtualtable.less +++ b/src/wtf/ui/virtualtable.less @@ -52,7 +52,6 @@ .noSelect(); width: 100%; height: 28px; - background-color: red; background-clip:padding-box; background-color: rgba(0,0,0,0.2); .insetBoxShadow2(1px, 1px, 0px, rgba(0,0,0,0.10), diff --git a/src/wtf/ui/zoom/element.js b/src/wtf/ui/zoom/element.js index f64fcdab..fb685f72 100644 --- a/src/wtf/ui/zoom/element.js +++ b/src/wtf/ui/zoom/element.js @@ -83,7 +83,7 @@ wtf.ui.zoom.Element = function(el, zoomTarget) { this.elementOffset_ = goog.style.getPageOffset(this.el); // Relayout as required. - this.listen( + this.listenWithScope( this.viewportSizeMonitor_, goog.events.EventType.RESIZE, function() { diff --git a/src/wtf/util/BUILD b/src/wtf/util/BUILD new file mode 100644 index 00000000..12bb1049 --- /dev/null +++ b/src/wtf/util/BUILD @@ -0,0 +1,22 @@ +# Description: +# Utilities. + +package(default_visibility = ["//:internal"]) + +load("//builddefs:config.bzl", "COMMON_JS_SUPPRESSIONS") +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library") + +closure_js_library( + name = "util", + srcs = [ + "canvas.js", + "functionbuilder.js", + "options.js", + "util.js", + ], + suppress = COMMON_JS_SUPPRESSIONS, + deps = [ + "//src/wtf/events:eventemitter", + "@io_bazel_rules_closure//closure/library", + ], +) diff --git a/src/wtf/version.js b/src/wtf/version.js index 2c9a2cf3..b4af1517 100644 --- a/src/wtf/version.js +++ b/src/wtf/version.js @@ -24,7 +24,7 @@ goog.provide('wtf.version'); */ wtf.version.getValue = function() { // Set via update-version.sh - return 1436947200000; // time + return 1508400000000; // time }; @@ -34,7 +34,7 @@ wtf.version.getValue = function() { */ wtf.version.getCommit = function() { // Set via update-version.sh - return 'b42eb485660d79fd6756d34835427a8b39170227'; // sha + return '2ee6617cf306be390dd8ba8e11d63dd63723ee84'; // sha }; @@ -45,7 +45,7 @@ wtf.version.getCommit = function() { */ wtf.version.toString = function() { // Set via update-version.sh - return '2015.7.15-1'; // string + return '2017.10.19-1'; // string }; diff --git a/src/wtf/wtf.js b/src/wtf/wtf.js index fbc66295..ac305195 100644 --- a/src/wtf/wtf.js +++ b/src/wtf/wtf.js @@ -215,8 +215,12 @@ wtf.now = (function() { * @return {number} Estimated overhead, in nanoseconds (1/1000 us). */ wtf.computeNowOverhead = function() { - // This is in a function so that v8 can JIT it easier. - // We then run it a few times to try to factor out the JIT time. + /** + * This is in a function so that v8 can JIT it easier. + * We then run it a few times to try to factor out the JIT time. + * @param {number} iterations + * @return {number} + */ function computeInner(iterations) { var dummy = 0; for (var n = 0; n < iterations; n++) { @@ -225,7 +229,7 @@ wtf.computeNowOverhead = function() { dummy += wtf.now(); } return dummy; - }; + } var iterations = 100000; var dummy = 0; diff --git a/test/test-js.html b/test/test-js.html index 2268640c..65b1d409 100644 --- a/test/test-js.html +++ b/test/test-js.html @@ -2,8 +2,8 @@ WTF Test (normal JS) - - + +